0+,+'+{"files":{"assets":{"files":{"blank.ico":{"size":370070,"offset":"0"},"certificate.html":{"size":3032,"offset":"370070"},"error-icon.png":{"size":2533,"offset":"373102"},"error.html":{"size":3132,"offset":"375635"},"login.html":{"size":5081,"offset":"378767"}}},"index.js":{"size":31507,"offset":"383848"},"js-adapter":{"files":{"js-adapter.js":{"size":267975,"offset":"415355"},"src":{"files":{"api":{"files":{"application":{"files":{"application.js":{"size":7400,"offset":"683330"},"applicationOption.js":{"size":79,"offset":"690730"}}},"base.js":{"size":6238,"offset":"690809"},"clipboard":{"files":{"clipboard.js":{"size":1284,"offset":"697047"},"write-request.js":{"size":79,"offset":"698331"}}},"events":{"files":{"application.js":{"size":79,"offset":"698410"},"base.js":{"size":221,"offset":"698489"},"channel.js":{"size":79,"offset":"698710"},"emitterMap.js":{"size":864,"offset":"698789"},"eventAggregator.js":{"size":2115,"offset":"699653"},"externalApplication.js":{"size":79,"offset":"701768"},"frame.js":{"size":79,"offset":"701847"},"globalHotkey.js":{"size":79,"offset":"701926"},"notifications.js":{"size":79,"offset":"702005"},"system.js":{"size":79,"offset":"702084"},"window.js":{"size":79,"offset":"702163"}}},"external-application":{"files":{"external-application.js":{"size":811,"offset":"702242"}}},"fin.js":{"size":1470,"offset":"703053"},"frame":{"files":{"frame.js":{"size":1026,"offset":"704523"}}},"global-hotkey":{"files":{"index.js":{"size":1756,"offset":"705549"}}},"interappbus":{"files":{"channel":{"files":{"channel.js":{"size":3002,"offset":"707305"},"client.js":{"size":812,"offset":"710307"},"index.js":{"size":6697,"offset":"711119"},"provider.js":{"size":1217,"offset":"717816"}}},"interappbus.js":{"size":4675,"offset":"719033"}}},"notification":{"files":{"notification.js":{"size":3179,"offset":"723708"}}},"system":{"files":{"application.js":{"size":79,"offset":"726887"},"clearCacheOption.js":{"size":79,"offset":"726966"},"cookie.js":{"size":79,"offset":"727045"},"crashReporterOption.js":{"size":79,"offset":"727124"},"download-asset.js":{"size":79,"offset":"727203"},"download-preload.js":{"size":79,"offset":"727282"},"entity.js":{"size":79,"offset":"727361"},"external-process.js":{"size":79,"offset":"727440"},"host-specs.js":{"size":79,"offset":"727519"},"log.js":{"size":79,"offset":"727598"},"monitor.js":{"size":79,"offset":"727677"},"point.js":{"size":79,"offset":"727756"},"process.js":{"size":79,"offset":"727835"},"proxy.js":{"size":79,"offset":"727914"},"registry-info.js":{"size":79,"offset":"727993"},"runtime-info.js":{"size":79,"offset":"728072"},"rvm.js":{"size":79,"offset":"728151"},"system.js":{"size":11952,"offset":"728230"},"window.js":{"size":79,"offset":"740182"}}},"window":{"files":{"anchor-type.js":{"size":79,"offset":"740261"},"bounds.js":{"size":79,"offset":"740340"},"transition.js":{"size":79,"offset":"740419"},"window.js":{"size":11259,"offset":"740498"},"windowOption.js":{"size":79,"offset":"751757"}}}}},"environment":{"files":{"environment.js":{"size":155,"offset":"751836"},"node-env.js":{"size":1255,"offset":"751991"}}},"identity.js":{"size":79,"offset":"753246"},"launcher":{"files":{"launcher.js":{"size":2350,"offset":"753325"},"nix-launch.js":{"size":3848,"offset":"755675"},"util.js":{"size":5056,"offset":"759523"},"win-launch.js":{"size":2940,"offset":"764579"}}},"main.js":{"size":1659,"offset":"767519"},"transport":{"files":{"port-discovery.js":{"size":10520,"offset":"769178"},"transport-errors.js":{"size":1205,"offset":"779698"},"transport.js":{"size":5875,"offset":"780903"},"websocket.js":{"size":1715,"offset":"786778"},"wire.js":{"size":1487,"offset":"788493"}}},"util":{"files":{"normalize-config.js":{"size":2238,"offset":"789980"},"promises.js":{"size":782,"offset":"792218"},"ref-counter.js":{"size":1432,"offset":"793000"}}}}}}},"node_modules":{"files":{"minimist":{"files":{"index.js":{"size":7189,"offset":"794432"}}},"options":{"files":{"lib":{"files":{"options.js":{"size":2330,"offset":"801621"}}},"Makefile":{"size":208,"offset":"803951"},"package.json":{"size":1353,"offset":"804159"},"README.md":{"size":2183,"offset":"805512"}}},"rx":{"files":{"index.js":{"size":144045,"offset":"807695"}}},"ultron":{"files":{"index.js":{"size":3109,"offset":"951740"},"LICENSE":{"size":1115,"offset":"954849"},"package.json":{"size":1856,"offset":"955964"},"README.md":{"size":4014,"offset":"957820"}}},"underscore":{"files":{"index.js":{"size":18069,"offset":"961834"}}},"ws":{"files":{"index.js":{"size":1055,"offset":"979903"},"lib":{"files":{"BufferPool.js":{"size":1775,"offset":"980958"},"BufferUtil.fallback.js":{"size":1414,"offset":"982733"},"BufferUtil.js":{"size":252,"offset":"984147"},"ErrorCodes.js":{"size":713,"offset":"984399"},"Extensions.js":{"size":1568,"offset":"985112"},"PerMessageDeflate.js":{"size":9432,"offset":"986680"},"Receiver.hixie.js":{"size":4228,"offset":"996112"},"Receiver.js":{"size":23184,"offset":"1000340"},"Sender.hixie.js":{"size":2972,"offset":"1023524"},"Sender.js":{"size":8062,"offset":"1026496"},"Validation.fallback.js":{"size":201,"offset":"1034558"},"Validation.js":{"size":256,"offset":"1034759"},"WebSocket.js":{"size":27620,"offset":"1035015"},"WebSocketServer.js":{"size":16935,"offset":"1062635"}}},"Makefile":{"size":993,"offset":"1079570"},"node_modules":{"files":{"ultron":{"files":{"index.js":{"size":2962,"offset":"1080563"},"LICENSE":{"size":1115,"offset":"1083525"},"package.json":{"size":1834,"offset":"1084640"},"README.md":{"size":3707,"offset":"1086474"},"test.js":{"size":8590,"offset":"1090181"}}}}},"package.json":{"size":1724,"offset":"1098771"},"README.md":{"size":7119,"offset":"1100495"},"SECURITY.md":{"size":1533,"offset":"1107614"}}}}},"package.json":{"size":1852,"offset":"1109147"},"src":{"files":{"browser":{"files":{"animation":{"files":{"tween.js":{"size":4802,"offset":"1110999"}}},"animations.js":{"size":13050,"offset":"1115801"},"api":{"files":{"application.js":{"size":36730,"offset":"1269982"},"channel.js":{"size":8443,"offset":"1306712"},"external_application.js":{"size":3741,"offset":"1315155"},"file_download.js":{"size":4297,"offset":"1318896"},"frame.js":{"size":3110,"offset":"1323193"},"global_hotkey.js":{"size":4492,"offset":"1326303"},"interappbus.js":{"size":8196,"offset":"1330795"},"notification.js":{"size":441,"offset":"1338991"},"notifications":{"files":{"note_action.js":{"size":1671,"offset":"1339432"},"observable_sequences.js":{"size":1571,"offset":"1341103"},"shapes.js":{"size":1530,"offset":"1342674"},"subscriptions.js":{"size":19241,"offset":"1344204"}}},"system.js":{"size":24320,"offset":"1363445"},"window.js":{"size":76909,"offset":"1387765"}}},"api_protocol":{"files":{"api_handlers":{"files":{"api_policy_processor.js":{"size":12566,"offset":"1128851"},"api_protocol_base.js":{"size":4836,"offset":"1141417"},"application.js":{"size":17303,"offset":"1146253"},"authorization.js":{"size":7316,"offset":"1163556"},"channel.js":{"size":3426,"offset":"1170872"},"clipboard.js":{"size":2880,"offset":"1174298"},"deprecated_external_windowing_middleware.js":{"size":3062,"offset":"1177178"},"event_listener.js":{"size":7874,"offset":"1180240"},"external_application.js":{"size":636,"offset":"1188114"},"frame.js":{"size":762,"offset":"1188750"},"global_hotkey.js":{"size":1745,"offset":"1189512"},"grouped_window_moves.js":{"size":2944,"offset":"1191257"},"interappbus.js":{"size":6930,"offset":"1194201"},"mesh_middleware.js":{"size":7081,"offset":"1201131"},"middleware_entity_existence.js":{"size":1572,"offset":"1208212"},"notifications.js":{"size":2363,"offset":"1209784"},"system.js":{"size":18769,"offset":"1212147"},"window.js":{"size":18143,"offset":"1230916"}}},"index.js":{"size":2530,"offset":"1249059"},"shapes.js":{"size":79,"offset":"1251589"},"transport_strategy":{"files":{"ack.js":{"size":1303,"offset":"1251668"},"api_transport_base.js":{"size":817,"offset":"1252971"},"base_handler.js":{"size":1256,"offset":"1253788"},"elipc_strategy.js":{"size":10521,"offset":"1255044"},"ws_strategy.js":{"size":4417,"offset":"1265565"}}}}},"authentication_delegate.js":{"size":1825,"offset":"1464674"},"bounds_changed_state_tracker.js":{"size":21756,"offset":"1466499"},"cached_resource_fetcher.js":{"size":11022,"offset":"1488255"},"clip_bounds.js":{"size":666,"offset":"1499277"},"connection_manager.js":{"size":2138,"offset":"1499943"},"convert_options.js":{"size":11719,"offset":"1502081"},"core_state.js":{"size":22412,"offset":"1513800"},"deferred.js":{"size":1003,"offset":"1536212"},"disabled_frame_group_tracker.js":{"size":16013,"offset":"1537215"},"external_window_event_adapter.js":{"size":6725,"offset":"1553228"},"int_pool.js":{"size":684,"offset":"1559953"},"log.js":{"size":1581,"offset":"1560637"},"monitor_info.js":{"size":7825,"offset":"1562218"},"navigation_validation.js":{"size":3555,"offset":"1570043"},"normalized_rectangle.js":{"size":2832,"offset":"1573598"},"of_events.js":{"size":5108,"offset":"1576430"},"port_discovery.js":{"size":3012,"offset":"1581538"},"preload_scripts.js":{"size":4081,"offset":"1584550"},"process_tracker.js":{"size":12987,"offset":"1588631"},"rectangle.js":{"size":19370,"offset":"1601618"},"remote_subscriptions.js":{"size":8454,"offset":"1620988"},"runtime_p2p":{"files":{"emitter_map.js":{"size":887,"offset":"1629442"},"peer_connection_manager.js":{"size":4267,"offset":"1630329"}}},"rvm":{"files":{"runtime_initiated_topics":{"files":{"app_assets.js":{"size":5075,"offset":"1634596"},"rvm_info.js":{"size":3789,"offset":"1639671"}}},"rvm_message_bus.js":{"size":6851,"offset":"1643460"},"utils.js":{"size":3103,"offset":"1650311"}}},"session.js":{"size":3956,"offset":"1653414"},"subscription_manager.js":{"size":3289,"offset":"1657370"},"transport.js":{"size":503,"offset":"1660659"},"transports":{"files":{"base.js":{"size":462,"offset":"1661162"},"chromium_ipc.js":{"size":1240,"offset":"1661624"},"chromium_socket.js":{"size":7027,"offset":"1662864"},"electron_ipc.js":{"size":288,"offset":"1669891"},"socket_server.js":{"size":4085,"offset":"1670179"},"unix_domain_socket.js":{"size":4605,"offset":"1674264"},"wm_copydata.js":{"size":1974,"offset":"1678869"}}},"utils.js":{"size":1716,"offset":"1680843"},"web_request_handler.js":{"size":2573,"offset":"1682559"},"window_group_transaction_tracker.js":{"size":720,"offset":"1685132"},"window_groups_runtime_proxy.js":{"size":8344,"offset":"1685852"},"window_groups.js":{"size":11568,"offset":"1694196"}}},"common":{"files":{"errors.js":{"size":3995,"offset":"1705764"},"main.js":{"size":3026,"offset":"1709759"},"route.js":{"size":1409,"offset":"1712785"},"safe_int.js":{"size":645,"offset":"1714194"},"timer.js":{"size":671,"offset":"1714839"}}},"renderer":{"files":{"api-decorator.js":{"size":23451,"offset":"1715510"},"extended":{"files":{"asar.js":{"size":69,"offset":"1738961"},"fs.js":{"size":279,"offset":"1739030"},"process.js":{"size":58,"offset":"1739309"}}},"main.js":{"size":2783,"offset":"1739367"},"node-less.js":{"size":7084,"offset":"1742150"}}},"shapes.js":{"size":262,"offset":"1749234"}}}}} hf ¨Î00 ¨%v@@ (B;€€ (F} ( n…(  @˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙( @ €˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙(0` €%˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙(@€ B˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙(€ ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙( ˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙˙ Select a Certificate ×
Select a Certificate

‰PNG  IHDR;0®˘gAMA±Ź üa cHRMz&€„ú€ču0ę`:pśşQ< pHYs  šśYiTXtXML:com.adobe.xmp 1 LÂ'YöIDATH ĄWmlUwžóroďą…˛®´¤bBíŘ[›tA6  ł“M?h\şĹ©_TŁK Q7XćÜÄĚ/25Śa”vmçh»¶´÷ŢŢóöř{ţçŢK5ž›sĎ9˙ó<Ďďů?ď‡čÇ^"[vu›hŽ—ś„űé‡ň(ďôŐéO<ý±ňÄo­ űţ?PX^Ůł¨ţ¶yntß ›—Ąm«É"™IăGWŠŚGrüýŔţăĎ]~§ĚdDx–ňZůz 0(Ö“Ĺű ˝¶¸vUSš;j\jŻsą&k1¦*5 } ůđűĹh×gĎ~ř†RÉzě|Ĺŕ™>MĆ"k;±?ůŃ›çn¨µ:R´m~‘/B~Ś?â8ž&+`€?¬3»x(ĆňOźşö~(ŰôŹá@nuB.äC˝ä¨«D,©Śx÷Âş;ďśI]÷ě¶ ö)ă f×R†˛„«WUG, c€»)ŰĄżçăGŻĐÖŽžˇż*ŻľżŇU'1o ôîzµ5Ëm…Č&«ęŁ(ÓčŠD6‹ň©n‰’WŻşLĐD™zw23;*DAĐšµÚî­‘Wu# Ő…%ž$ň4¤HÍ»¶–÷.Íň˘‰(.îL—OöX42LÜ8Ź(*B>⍧¬b&kś©%:ÓMôÁ°76Za”÷禝›‹î©r2V-Ś)Ö‹}[{áürôn¨u:ŐĽa„ÎŚ”uĽŹyí#Ä+×’ś‚@7‹$Ă%¸şYŐ5Ľ“ÝÄík~Ľˇ]“š`‰ÇmjąL¤XšjÎúĄ°ů>BôÖŻjLÇ[8¤0SoŃńw™?÷d:lÂ8/1É/Ađ'r—`¸R¦Y‘WGrř$ńćŐä}÷'PL¨@–Äös¸ěVË/\¦Ć”ł ˛OŁ}ýř…w$‘Ö”Ž:nI3ű$¨bsěQÄNî- Č{z'ńĆU€ĽzîŇÓkH@7=@^çNC«ë…Ě)H‚¬@$€ljJŰşkĹ4ÎŇâĐâÉ3Y‹ŞBu{0ańGć±9Ja7ŮË–“»°…ě;>IáĄ3$Ž/n&JU“üţj'ďŮź’ŰĽ‚žó”˙öWI$^ÚÂ49$Gő·|’ąwWWúőpî’ţZcŐçd¬uH2MT"J5°ł×ŹQ8x΀şÍ q˝ŕgţ6Qďe€b§ĎţŚÜ-ôö$ p Ý5źŘOŠ Äiî{WĺĂřäîÁÂ18 (Y¨3Đ(G˛‚çň«7P™#ď{¸÷ç)WŘlôóľ˙<ĚŰś€>ő8ÉKo&qÂ. »Ĺ~‰â¶–\ĹÔU;es’ďń3‡$ QÁ··’Ľü;ĘÓăT}žý዆ҀöżGą§ľBd@Ak‚χĺ\#´$R‹Ą…‹ ŕ9i‡|ĹM,Ł÷É5z'.b ţ&ĺ&żĐÝĆçJôtSîë[öIh”V3`*hIšŇðŔ¦>”÷iî˙Ă«MJvQRłŤĘÂŐb,x=YĄȇ Ĺâ+Jµ)"ő »µ c¶)SŠŢçöÝęNýŢnJ5·Pvç/d&©ZŇÜ®™˝,;Ö…"€żč§±˘0›Ŕ0IA5e]‰â€”Ń@ę‡OaŢü“[Ě˝És¤?ú€ˇĄllšId”˝ŮŠQŚd@1ňhây´kĽŃmă$¸lT­H›LR»3)óÍÇŚOé7G(˙Ť/cí<Ň ŃŽ´ŞđjţŞ,•¨˛C]0yüé›Ň>*ů¦›\®‚)r12®őą—xÝCä=ó\R4O+)ÓJÜŚŞ…|ÖĽÖââ6ŚěŰ—S8ÔCňö1â9sá­6aĹU[;]°źÖ˘ŕćřŰmł_‰?Ő W–ĎňGV,’ŃŠG7ŻżŻWôđĎwËč¦Ő2íGŰ[eôžşä\Ůš¬m\šs mß{2şy­‘1˛âV#3ľŻ^ŁŚgé`¦EŢŐ?“k!P·Ä‡˘¶ŤĚř±ŹňŰź@!ů­)’»śPÍGőŇ"óŠĚwž0´x [Ú"EHb‰TfQ€íR,ĹdmQŕl]řS[Ý wͰ¶ĺŃÄC§ĆˇwúС>\ŽP{_'Z2ßT3ŃÂbôS)h‘$Ňx8ÝGüZ¨–‡×öµÍ' =Űu˙2˙|ů‰ˇ/)޶b“UÚśµOę °u6Ôéc" ‹ÚOétS 8‹[0xŤA %ePeŕ(Ąť®UÍ"9ŰŤĐĹňR€Fă~µm§Oĺâ]˛FçŻ2–VnLf˛Ü…1EÇ•%žÝ¤MÜôf´6BOE€TOű¸–jĄ¦ĎhˤČ)\Ž«×=“ŹţĺŘÚOÍX‹.RĹ©śĐV`Ö”ÉŘŽ}Ágé.HמaéřV˙đ» 4ăíý XŤ’ćZůŃuz˝´Ďv¸ĆŞŘŮ´C5Đâ0„< ĺđ@Qţ·~Š´˙ ă↟0j ŘVh¬(20Čń‹ř„Ůř_~ÂLÁşöVSMsîÚ7ZńţżŹ¶¶Ë·˝lDIEND®B`‚ Error
Authentication Required ×
Authentication Required
'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 _window = require('./src/browser/api/window'); var _authentication_delegate = require('./src/browser/authentication_delegate'); var _of_events = require('./src/browser/of_events'); var _of_events2 = _interopRequireDefault(_of_events); var _port_discovery = require('./src/browser/port_discovery'); var _global_hotkey = require('./src/browser/api/global_hotkey'); var _connection_manager = require('./src/browser/connection_manager'); var _connection_manager2 = _interopRequireDefault(_connection_manager); var _log = require('./src/browser/log'); var log = _interopRequireWildcard(_log); var _remote_subscriptions = require('./src/browser/remote_subscriptions'); var _route = require('./src/common/route'); var _route2 = _interopRequireDefault(_route); var _file_download = require('./src/browser/api/file_download'); 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 }; } /* index.js */ // built\-in modules var fs = require('fs'); var path = require('path'); var electron = require('electron'); var app = electron.app; // Module to control application life. var BrowserWindow = electron.BrowserWindow; var crashReporter = electron.crashReporter; var globalShortcut = electron.globalShortcut; var ipc = electron.ipcMain; var Menu = electron.Menu; // npm modules var _ = require('underscore'); var minimist = require('minimist'); // local modules var Application = require('./src/browser/api/application.js').Application; var System = require('./src/browser/api/system.js').System; var apiProtocol = require('./src/browser/api_protocol'); var socketServer = require('./src/browser/transports/socket_server').server; var convertOptions = require('./src/browser/convert_options.js'); var coreState = require('./src/browser/core_state.js'); var webRequestHandlers = require('./src/browser/web_request_handler.js'); var errors = require('./src/common/errors.js'); // locals var firstApp = null; var rvmBus = void 0; var otherInstanceRunning = false; var appIsReady = false; var deferredLaunches = []; var resolveServerReady = void 0; var serverReadyPromise = new Promise(function (resolve) { resolveServerReady = function resolveServerReady() { return resolve(); }; }); app.on('child-window-created', function (parentId, childId, childOptions) { if (!coreState.addChildToWin(parentId, childId)) { console.warn('failed to add'); } _window.Window.create(childId, childOptions); }); app.on('select-client-certificate', function (event, webContents, url, list, callback) { // No need to choose if there are // fewer than two certificates if (list.length < 2) { return; } event.preventDefault(); var clientCertDialog = new BrowserWindow({ width: 450, height: 280, show: false, frame: false, skipTaskbar: true, resizable: false, alwaysOnTop: true, webPreferences: { nodeIntegration: true, openfinIntegration: false } }); var ipcUuid = app.generateGUID(); var ipcTopic = 'client-certificate-selection/' + ipcUuid; function resolve(cert) { cleanup(); callback(cert); } function cleanup() { ipc.removeListener(ipcTopic, onClientCertificateSelection); clientCertDialog.removeListener('closed', onClosed); } function onClientCertificateSelection(event, index) { if (index >= 0 && index < list.length) { resolve(list[index]); clientCertDialog.close(); } } function onClosed() { resolve({}); // NOTE: Will cause a page load failure } ipc.on(ipcTopic, onClientCertificateSelection); clientCertDialog.on('closed', onClosed); var params = '?url=' + encodeURIComponent(url) + '&uuid=' + encodeURIComponent(ipcUuid) + '&certs=' + encodeURIComponent(_.pluck(list, 'issuerName')); clientCertDialog.loadURL(path.resolve(__dirname, 'assets', 'certificate.html') + params); }); _port_discovery.portDiscovery.on(_route2.default.runtime('launched'), function (portInfo) { //check if the ports match: var myPortInfo = coreState.getSocketServerState(); var myUuid = (0, _connection_manager.getMeshUuid)(); log.writeToLog('info', 'Port discovery message received ' + JSON.stringify(portInfo)); //TODO include old runtimes in the determination. if (_connection_manager.meshEnabled && portInfo.port !== myPortInfo.port && (0, _connection_manager.isMeshEnabledRuntime)(portInfo)) { _connection_manager2.default.connectToRuntime(myUuid, portInfo).then(function (runtimePeer) { //one connected we broadcast our port discovery message. staggerPortBroadcast(myPortInfo); log.writeToLog('info', 'Connected to runtime ' + JSON.stringify(runtimePeer.portInfo)); (0, _remote_subscriptions.applyAllRemoteSubscriptions)(runtimePeer); }).catch(function (err) { log.writeToLog('info', 'Failed to connect to runtime ' + JSON.stringify(portInfo) + ', ' + JSON.stringify(errors.errorToPOJO(err))); }); } }); includeFlashPlugin(); // Enable Single tenant for MAC handleMacSingleTenant(); // Opt in to launch crash reporter initializeCrashReporter(coreState.argo); // Safe errors initialization errors.initSafeErrors(coreState.argo); // Has a local copy of an app config if (coreState.argo['local-startup-url']) { try { var localConfig = JSON.parse(fs.readFileSync(coreState.argo['local-startup-url'])); if (typeof localConfig['devtools_port'] === 'number') { if (!coreState.argo['remote-debugging-port']) { log.writeToLog(1, 'remote-debugging-port: ' + localConfig['devtools_port'], true); app.commandLine.appendSwitch('remote-debugging-port', localConfig['devtools_port'].toString()); } else { log.writeToLog(1, 'Ignoring devtools_port from manifest', true); } } } catch (err) { console.error(err); } } var handleDelegatedLaunch = function handleDelegatedLaunch(commandLine) { var otherInstanceArgo = minimist(commandLine); initializeCrashReporter(otherInstanceArgo); // delegated args from a second instance launchApp(otherInstanceArgo, false); // Will queue if server is not ready. serverReadyPromise.then(function () { var socketServerState = coreState.getSocketServerState(); var portInfo = _port_discovery.portDiscovery.getPortInfoByArgs(otherInstanceArgo, socketServerState.port); _port_discovery.portDiscovery.broadcast(portInfo); }); // command line flag --delete-cache-on-exit rvmCleanup(otherInstanceArgo); return true; }; function handleDeferredLaunches() { deferredLaunches.forEach(function (commandLine) { handleDelegatedLaunch(commandLine); }); deferredLaunches.length = 0; } app.on('chrome-browser-process-created', function () { otherInstanceRunning = app.makeSingleInstance(function (commandLine) { log.writeToLog(1, 'chrome-browser-process-created callback ' + commandLine, true); var socketServerState = coreState.getSocketServerState(); if (appIsReady && socketServerState && socketServerState.port) { return handleDelegatedLaunch(commandLine); } else { deferredLaunches.push(commandLine); return true; } }); if (otherInstanceRunning) { if (appIsReady) { deleteProcessLogfile(true); } app.commandLine.appendArgument('noerrdialogs'); process.argv.push('--noerrdialogs'); app.exit(0); return; } }); // This method will be called when Electron has finished // initialization and is ready to create browser windows. app.on('ready', function () { appIsReady = true; if (otherInstanceRunning) { deleteProcessLogfile(true); app.quit(); return; } app.registerNamedCallback('convertToElectron', convertOptions.convertToElectron); app.registerNamedCallback('getWindowOptionsById', coreState.getWindowOptionsById); if (process.platform === 'win32') { log.writeToLog('info', 'group-policy build: ' + process.buildFlags.groupPolicy); log.writeToLog('info', 'enable-chromium build: ' + process.buildFlags.enableChromium); } log.writeToLog('info', 'build architecture: ' + process.arch); app.vlog(1, 'process.versions: ' + JSON.stringify(process.versions, null, 2)); rvmBus = require('./src/browser/rvm/rvm_message_bus').rvmMessageBus; app.allowNTLMCredentialsForAllDomains(true); if (process.platform === 'win32') { var integrityLevel = app.getIntegrityLevel(); System.log('info', 'Runtime integrity level of the app: ' + integrityLevel); } rotateLogs(coreState.argo); migrateCookies(); migrateLocalStorage(coreState.argo); //Once we determine we are the first instance running we setup the API's //Create the new Application. initServer(); webRequestHandlers.initHandlers(); launchApp(coreState.argo, true); registerShortcuts(); registerMacMenu(); app.on('activate', function () { // On OS X it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. launchApp(coreState.argo, true); }); //subscribe to auth requests: app.on('login', function (event, webContents, request, authInfo, callback) { var browserWindow = webContents.getOwnerBrowserWindow(); var ofWindow = coreState.getWinById(browserWindow.id).openfinWindow; var identity = { name: ofWindow._options.name, uuid: ofWindow._options.uuid }; var windowEvtName = _route2.default.window('auth-requested', identity.uuid, identity.name); var appEvtName = _route2.default.application('window-auth-requested', identity.uuid); (0, _authentication_delegate.addPendingAuthRequests)(identity, authInfo, callback); if (_of_events2.default.listeners(windowEvtName).length < 1 && _of_events2.default.listeners(appEvtName).length < 1) { (0, _authentication_delegate.createAuthUI)(identity); } else { _of_events2.default.emit(windowEvtName, { topic: 'window', type: 'auth-requested', uuid: identity.uuid, name: identity.name, authInfo: authInfo }); } event.preventDefault(); }); // native code in AtomRendererClient::ShouldFork app.on('enable-chromium-renderer-fork', function (event) { // @TODO it should be an option for app, not runtime->arguments if (coreState.argo['enable-chromium-renderer-fork']) { app.vlog(1, 'applying Chromium renderer fork'); event.preventDefault(); } }); rvmBus.on(_route2.default.rvmMessageBus('broadcast', 'download-asset', 'progress'), function (payload) { if (payload) { _of_events2.default.emit(_route2.default.system('asset-download-progress-' + payload.downloadId), { totalBytes: payload.totalBytes, downloadedBytes: payload.downloadedBytes }); } }); rvmBus.on(_route2.default.rvmMessageBus('broadcast', 'download-asset', 'error'), function (payload) { if (payload) { _of_events2.default.emit(_route2.default.system('asset-download-error-' + payload.downloadId), { reason: payload.error, err: errors.errorToPOJO(new Error(payload.error)) }); } }); rvmBus.on(_route2.default.rvmMessageBus('broadcast', 'download-asset', 'complete'), function (payload) { if (payload) { _of_events2.default.emit(_route2.default.system('asset-download-complete-' + payload.downloadId), { path: payload.path }); } }); rvmBus.on(_route2.default.rvmMessageBus('broadcast', 'application', 'runtime-download-progress'), function (payload) { if (payload) { _of_events2.default.emit(_route2.default.system('runtime-download-progress-' + payload.downloadId), payload); } }); rvmBus.on(_route2.default.rvmMessageBus('broadcast', 'application', 'runtime-download-error'), function (payload) { if (payload) { _of_events2.default.emit(_route2.default.system('runtime-download-error-' + payload.downloadId), { reason: payload.error, err: errors.errorToPOJO(new Error(payload.error)) }); } }); rvmBus.on(_route2.default.rvmMessageBus('broadcast', 'application', 'runtime-download-complete'), function (payload) { if (payload) { _of_events2.default.emit(_route2.default.system('runtime-download-complete-' + payload.downloadId), { path: payload.path }); } }); try { electron.session.defaultSession.on('will-download', function (event, item, webContents) { try { var _webContents$browserW = webContents.browserWindowOptions, uuid = _webContents$browserW.uuid, name = _webContents$browserW.name; var downloadListener = (0, _file_download.createWillDownloadEventListener)({ uuid: uuid, name: name }); downloadListener(event, item, webContents); } catch (err) { log.writeToLog('info', 'Error while processing will-download event.'); log.writeToLog('info', err); } }); } catch (err) { log.writeToLog('info', 'Could not wire up File Download API'); log.writeToLog('info', err); } handleDeferredLaunches(); }); // end app.ready function staggerPortBroadcast(myPortInfo) { setTimeout(function () { try { _port_discovery.portDiscovery.broadcast(myPortInfo); } catch (e) { log.writeToLog('info', e); } }, Math.floor(Math.random() * 50)); } function includeFlashPlugin() { var pluginName = void 0; switch (process.platform) { case 'win32': pluginName = 'pepflashplayer.dll'; break; case 'darwin': pluginName = 'PepperFlashPlayer.plugin'; break; case 'linux': pluginName = 'libpepflashplayer.so'; break; default: pluginName = ''; break; } if (pluginName) { app.commandLine.appendSwitch('ppapi-flash-path', path.join(process.resourcesPath, 'plugins', 'flash', pluginName)); // Currently for enable_chromium build the flash version need to be // specified. See RUN-4510 and RUN-4580. app.commandLine.appendSwitch('ppapi-flash-version', '30.0.0.154'); } } function initializeCrashReporter(argo) { if (!needsCrashReporter(argo)) { return; } var configUrl = argo['startup-url'] || argo['config']; var diagnosticMode = argo['diagnostics'] || false; var sandboxDisabled = argo['sandbox'] === false; // means '--no-sandbox' flag exists if (diagnosticMode && !sandboxDisabled) { log.writeToLog('info', '\'--no-sandbox\' flag has been automatically added, ' + 'because the application is running in diagnostics mode and has \'--diagnostics\' flag specified'); app.commandLine.appendSwitch('no-sandbox'); } crashReporter.startOFCrashReporter({ diagnosticMode: diagnosticMode, configUrl: configUrl }); } function rotateLogs(argo) { // only keep the 7 most recent logfiles System.getLogList(function (err, files) { if (err) { System.log('error', 'logfile error: ' + err); } else { files.filter(function (file) { return !(file.name === 'debug.log' || file.name.indexOf('debugp') === 0); }).sort(function (a, b) { return b.date - a.date; }).slice(6).forEach(function (file) { var filepath = path.join(app.getPath('userData'), file.name); fs.unlink(filepath, function (err) { if (err) { System.log('error', 'cannot delete logfile: ' + filepath); } else { System.log('info', 'deleting logfile: ' + filepath); } }); }); } }); app.reopenLogfile(); // delete debugp????.log file deleteProcessLogfile(false); rvmCleanup(argo); } function deleteProcessLogfile(closeLogfile) { var filename = app.getProcessLogfileName(); if (!filename) { System.log('info', 'process logfile name is undefined'); System.log('info', coreState.argo); return; } var filepath = path.join(app.getPath('userData'), filename); if (closeLogfile) { app.closeLogfile(); } try { fs.unlinkSync(filepath); System.log('info', 'deleting process logfile: ' + filepath); } catch (e) { System.log('error', 'cannot delete process logfile: ' + filepath); } } function rvmCleanup(argo) { var deleteCacheOnExitFlag = 'delete-cache-on-exit'; // notify RVM with necessary information to clean up cache folders on exit when we're called with --delete-cache-on-exit var deleteCacheOnExit = argo[deleteCacheOnExitFlag]; if (deleteCacheOnExit) { System.deleteCacheOnExit(function () { console.log('Successfully sent a delete-cache-on-exit message to the RVM.'); }, function (err) { console.log(err); }); } } function migrateLocalStorage(argo) { var oldLocalStoragePath = argo['old-local-storage-path'] || ''; var newLocalStoragePath = argo['new-local-storage-path'] || ''; var localStorageUrl = argo['local-storage-url'] || ''; if (oldLocalStoragePath && newLocalStoragePath && localStorageUrl) { try { System.log('info', 'Migrating Local Storage from ' + oldLocalStoragePath + ' to ' + newLocalStoragePath); app.migrateLocalStorage(oldLocalStoragePath, newLocalStoragePath, localStorageUrl); System.log('info', 'Migrated Local Storage'); } catch (e) { System.log('error', 'Couldn\'t migrate cache from ' + oldLocalStoragePath + ' to ' + newLocalStoragePath); System.log('error', e); } } } function initServer() { var attemptedHardcodedPort = false; apiProtocol.initApiHandlers(); socketServer.on('server/error', function (err) { // Guard against non listen errors and infinite retries. if (err && err.syscall === 'listen' && !attemptedHardcodedPort) { // Assuming connection issue. Bind on any available port console.log('Assuming connection issue. Bind on any available port'); attemptedHardcodedPort = true; socketServer.start(0); } }); socketServer.on('server/open', function (port) { console.log('Opened on', port); _port_discovery.portDiscovery.broadcast(_port_discovery.portDiscovery.getPortInfoByArgs(coreState.argo, port)); resolveServerReady(); handleDeferredLaunches(); }); socketServer.on('connection/message', function (id, message) { console.log('Receieved message', message); }); return socketServer; } //TODO: this function actually does more than just launch apps, it will initiate the web socket server and //is essential for proper runtime startup and adapter connectivity. we want to split into smaller independent parts. //please see the discussion on https://github.com/openfin/runtime-core/pull/194 function launchApp(argo, startExternalAdapterServer) { if (needsCrashReporter(argo)) { log.setToVerbose(); } convertOptions.fetchOptions(argo, function (configuration) { var configUrl = configuration.configUrl, configObject = configuration.configObject, _configuration$config = configuration.configObject, licenseKey = _configuration$config.licenseKey, _configuration$config2 = _configuration$config.shortcut, shortcut = _configuration$config2 === undefined ? {} : _configuration$config2; coreState.setManifest(configUrl, configObject); if (argo['user-app-config-args']) { var tempUrl = configObject['startup_app'].url; var delimiter = tempUrl.indexOf('?') < 0 ? '?' : '&'; configObject['startup_app'].url = '' + tempUrl + delimiter + argo['user-app-config-args']; } var startupAppOptions = convertOptions.getStartupAppOptions(configObject); var uuid = startupAppOptions && startupAppOptions.uuid; var name = startupAppOptions && startupAppOptions.name; var ofApp = Application.wrap(uuid); var ofManifestUrl = ofApp && ofApp._configUrl; var isRunning = Application.isRunning(ofApp); var company = shortcut.company, shortcutName = shortcut.name; var appUserModelId = void 0; var namePart = void 0; if (company) { namePart = shortcutName ? '.' + shortcutName : ''; appUserModelId = '' + company + namePart; } else { namePart = name ? '.' + name : ''; appUserModelId = '' + uuid + namePart; } app.setAppUserModelId(appUserModelId); // this ensures that external connections that start the runtime can do so without a main window var successfulInitialLaunch = true; // comparing ofManifestUrl and configUrl shouldn't consider query strings. Otherwise, it will break deep linking if (startupAppOptions && (!isRunning || ofManifestUrl.split('?')[0] !== configUrl.split('?')[0])) { //making sure that if a window is present we set the window name === to the uuid as per 5.0 startupAppOptions.name = uuid; successfulInitialLaunch = initFirstApp(configObject, configUrl, licenseKey); } else if (uuid) { Application.run({ uuid: uuid, name: uuid }, '', argo['user-app-config-args']); } if (startExternalAdapterServer && successfulInitialLaunch) { coreState.setStartManifest(configUrl, configObject); socketServer.start(configObject['websocket_port'] || 9696); } app.emit('synth-desktop-icon-clicked', { mouse: System.getMousePosition(), tickCount: app.getTickCount(), uuid: uuid }); }, function (error) { var title = errors.ERROR_TITLE_APP_INITIALIZATION; var type = errors.ERROR_BOX_TYPES.APP_INITIALIZATION; var args = { error: error, title: title, type: type }; errors.showErrorBox(args).catch(function (error) { return log.writeToLog('info', error); }).then(app.quit); }); } function initFirstApp(configObject, configUrl, licenseKey) { var startupAppOptions = void 0; var successfulLaunch = false; try { startupAppOptions = convertOptions.getStartupAppOptions(configObject); validatePreloadScripts(startupAppOptions); // Needs proper configs firstApp = Application.create(startupAppOptions, configUrl); coreState.setLicenseKey({ uuid: startupAppOptions.uuid }, licenseKey); Application.run({ uuid: firstApp.uuid }); firstApp.mainWindow.on('closed', function () { firstApp = null; }); successfulLaunch = true; } catch (error) { if (rvmBus) { rvmBus.publish({ topic: 'application', action: 'hide-splashscreen', sourceUrl: configUrl }); } var message = startupAppOptions.loadErrorMessage; var title = errors.ERROR_TITLE_APP_INITIALIZATION; var type = errors.ERROR_BOX_TYPES.APP_INITIALIZATION; var args = { error: error, message: message, title: title, type: type }; errors.showErrorBox(args).catch(function (error) { return log.writeToLog('info', error); }).then(function () { if (coreState.shouldCloseRuntime()) { app.quit(); } }); } return successfulLaunch; } //Please add any hotkeys added here to the the reservedHotKeys list. function registerShortcuts() { app.on('browser-window-focus', function (event, browserWindow) { var windowOptions = coreState.getWindowOptionsById(browserWindow.id); var accelerator = windowOptions && windowOptions.accelerator || {}; var webContents = browserWindow.webContents; if (accelerator.zoom) { var zoom = function zoom(increment) { return function () { webContents.send('zoom', { increment: increment }); }; }; globalShortcut.register('CommandOrControl+0', zoom(0)); globalShortcut.register('CommandOrControl+=', zoom(+1)); globalShortcut.register('CommandOrControl+Plus', zoom(+1)); globalShortcut.register('CommandOrControl+-', zoom(-1)); globalShortcut.register('CommandOrControl+_', zoom(-1)); } if (accelerator.devtools) { var devtools = function devtools() { webContents.openDevTools(); }; globalShortcut.register('CommandOrControl+Shift+I', devtools); } if (accelerator.reload) { var reload = function reload() { webContents.reload(); }; globalShortcut.register('F5', reload); globalShortcut.register('CommandOrControl+R', reload); } if (accelerator.reloadIgnoringCache) { var reloadIgnoringCache = function reloadIgnoringCache() { webContents.reloadIgnoringCache(); }; globalShortcut.register('Shift+F5', reloadIgnoringCache); globalShortcut.register('CommandOrControl+Shift+R', reloadIgnoringCache); } }); var unhookShortcuts = function unhookShortcuts(event, browserWindow) { if (!globalShortcut.isDestroyed()) { _global_hotkey.reservedHotKeys.forEach(function (a) { return globalShortcut.unregister(a); }); } }; app.on('browser-window-closed', unhookShortcuts); app.on('browser-window-blur', unhookShortcuts); } function registerMacMenu() { if (process.platform === 'darwin') { var template = [{ label: 'OpenFin', submenu: [{ role: 'quit' }] }, { role: 'editMenu' }]; var menu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(menu); } } // Set usrData & userCache path specifically for each application for MAC_OS function handleMacSingleTenant() { if (process.platform === 'darwin') { var configUrl = coreState.argo['startup-url'] || coreState.argo['config']; var cachePath = encodeURIComponent(configUrl); if (coreState.argo['security-realm']) { cachePath = path.join(cachePath, coreState.argo['security-realm']); } var userData = app.getPath('userData'); cachePath = path.join(userData, 'cache', cachePath, process.versions['openfin']); app.setPath('userData', cachePath); app.setPath('userCache', cachePath); } } function migrateCookies() { if (!process.buildFlags.enableChromium) { return; } var userData = app.getPath('userData'); var cookiePath = path.join(path.join(userData, 'Default')); var legacyCookiePath = userData; var cookieFile = path.join(path.join(cookiePath, 'Cookies')); var cookieJrFile = path.join(path.join(cookiePath, 'Cookies-journal')); var legacyCookieFile = path.join(path.join(legacyCookiePath, 'Cookies')); var legacyCookieJrFile = path.join(path.join(legacyCookiePath, 'Cookies-journal')); try { if (fs.existsSync(legacyCookieFile) && !fs.existsSync(cookieFile)) { log.writeToLog('info', 'migrating cookies from ' + legacyCookiePath + ' to ' + cookiePath); fs.copyFileSync(legacyCookieFile, cookieFile); fs.copyFileSync(legacyCookieJrFile, cookieJrFile); } else { log.writeToLog(1, 'skip cookie migration in ' + cookiePath, true); } } catch (err) { log.writeToLog('info', 'Error migrating cookies from ' + legacyCookiePath + ' to ' + cookiePath + ' ' + err); try { fs.unlinkSync(cookieFile); } catch (ignored) {} try { fs.unlinkSync(cookieJrFile); } catch (ignored) {} } } function needsCrashReporter(argo) { return !!(argo['diagnostics'] || argo['enable-crash-reporting']); } function validatePreloadScripts(options) { var name = options.name, uuid = options.uuid; var genErrorMsg = function genErrorMsg(propName) { return 'Invalid shape of \'' + propName + '\' window option. Please, consult the API documentation.'; }; var isValidPreloadScriptsArray = function isValidPreloadScriptsArray() { var v = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; return v.every(function (e) { return (typeof e === 'undefined' ? 'undefined' : _typeof(e)) === 'object' && typeof e.url === 'string'; }); }; if ('preload' in options) { log.writeToLog('info', '[preloadScripts] [' + uuid + ']-[' + name + ']: \'preload\' option ' + 'is deprecated, use \'preloadScripts\' instead'); if (Array.isArray(options.preload)) { if (!isValidPreloadScriptsArray(options.preload)) { throw new Error(genErrorMsg('preload')); } } else if (typeof options.preload !== 'string' && options.preload) { throw new Error(genErrorMsg('preload')); } } else if ('preloadScripts' in options) { if (Array.isArray(options.preloadScripts)) { if (!isValidPreloadScriptsArray(options.preloadScripts)) { throw new Error(genErrorMsg('preloadScripts')); } } else { if (options.preloadScripts) { throw new Error(genErrorMsg('preloadScripts')); } else { log.writeToLog('info', '[preloadScripts] [' + uuid + ']-[' + name + ']: Consider using an empty ' + 'array with \'preloadScripts\', instead of a falsy value'); } } } return true; } /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./src/of-main.ts"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./node_modules/base64-js/index.js": /*!*****************************************!*\ !*** ./node_modules/base64-js/index.js ***! \*****************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\n\nexports.byteLength = byteLength\nexports.toByteArray = toByteArray\nexports.fromByteArray = fromByteArray\n\nvar lookup = []\nvar revLookup = []\nvar Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array\n\nvar code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\nfor (var i = 0, len = code.length; i < len; ++i) {\n lookup[i] = code[i]\n revLookup[code.charCodeAt(i)] = i\n}\n\n// Support decoding URL-safe base64 strings, as Node.js does.\n// See: https://en.wikipedia.org/wiki/Base64#URL_applications\nrevLookup['-'.charCodeAt(0)] = 62\nrevLookup['_'.charCodeAt(0)] = 63\n\nfunction getLens (b64) {\n var len = b64.length\n\n if (len % 4 > 0) {\n throw new Error('Invalid string. Length must be a multiple of 4')\n }\n\n // Trim off extra bytes after placeholder bytes are found\n // See: https://github.com/beatgammit/base64-js/issues/42\n var validLen = b64.indexOf('=')\n if (validLen === -1) validLen = len\n\n var placeHoldersLen = validLen === len\n ? 0\n : 4 - (validLen % 4)\n\n return [validLen, placeHoldersLen]\n}\n\n// base64 is 4/3 + up to two characters of the original data\nfunction byteLength (b64) {\n var lens = getLens(b64)\n var validLen = lens[0]\n var placeHoldersLen = lens[1]\n return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction _byteLength (b64, validLen, placeHoldersLen) {\n return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen\n}\n\nfunction toByteArray (b64) {\n var tmp\n var lens = getLens(b64)\n var validLen = lens[0]\n var placeHoldersLen = lens[1]\n\n var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))\n\n var curByte = 0\n\n // if there are placeholders, only get up to the last complete 4 chars\n var len = placeHoldersLen > 0\n ? validLen - 4\n : validLen\n\n for (var i = 0; i < len; i += 4) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 18) |\n (revLookup[b64.charCodeAt(i + 1)] << 12) |\n (revLookup[b64.charCodeAt(i + 2)] << 6) |\n revLookup[b64.charCodeAt(i + 3)]\n arr[curByte++] = (tmp >> 16) & 0xFF\n arr[curByte++] = (tmp >> 8) & 0xFF\n arr[curByte++] = tmp & 0xFF\n }\n\n if (placeHoldersLen === 2) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 2) |\n (revLookup[b64.charCodeAt(i + 1)] >> 4)\n arr[curByte++] = tmp & 0xFF\n }\n\n if (placeHoldersLen === 1) {\n tmp =\n (revLookup[b64.charCodeAt(i)] << 10) |\n (revLookup[b64.charCodeAt(i + 1)] << 4) |\n (revLookup[b64.charCodeAt(i + 2)] >> 2)\n arr[curByte++] = (tmp >> 8) & 0xFF\n arr[curByte++] = tmp & 0xFF\n }\n\n return arr\n}\n\nfunction tripletToBase64 (num) {\n return lookup[num >> 18 & 0x3F] +\n lookup[num >> 12 & 0x3F] +\n lookup[num >> 6 & 0x3F] +\n lookup[num & 0x3F]\n}\n\nfunction encodeChunk (uint8, start, end) {\n var tmp\n var output = []\n for (var i = start; i < end; i += 3) {\n tmp =\n ((uint8[i] << 16) & 0xFF0000) +\n ((uint8[i + 1] << 8) & 0xFF00) +\n (uint8[i + 2] & 0xFF)\n output.push(tripletToBase64(tmp))\n }\n return output.join('')\n}\n\nfunction fromByteArray (uint8) {\n var tmp\n var len = uint8.length\n var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes\n var parts = []\n var maxChunkLength = 16383 // must be multiple of 3\n\n // go through the array every three bytes, we'll deal with trailing stuff later\n for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {\n parts.push(encodeChunk(\n uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)\n ))\n }\n\n // pad the end with zeros, but make sure to not forget the extra bytes\n if (extraBytes === 1) {\n tmp = uint8[len - 1]\n parts.push(\n lookup[tmp >> 2] +\n lookup[(tmp << 4) & 0x3F] +\n '=='\n )\n } else if (extraBytes === 2) {\n tmp = (uint8[len - 2] << 8) + uint8[len - 1]\n parts.push(\n lookup[tmp >> 10] +\n lookup[(tmp >> 4) & 0x3F] +\n lookup[(tmp << 2) & 0x3F] +\n '='\n )\n }\n\n return parts.join('')\n}\n\n\n//# sourceURL=webpack:///./node_modules/base64-js/index.js?"); /***/ }), /***/ "./node_modules/buffer/index.js": /*!**************************************!*\ !*** ./node_modules/buffer/index.js ***! \**************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("/* WEBPACK VAR INJECTION */(function(global) {/*!\n * The buffer module from node.js, for the browser.\n *\n * @author Feross Aboukhadijeh \n * @license MIT\n */\n/* eslint-disable no-proto */\n\n\n\nvar base64 = __webpack_require__(/*! base64-js */ \"./node_modules/base64-js/index.js\")\nvar ieee754 = __webpack_require__(/*! ieee754 */ \"./node_modules/ieee754/index.js\")\nvar isArray = __webpack_require__(/*! isarray */ \"./node_modules/isarray/index.js\")\n\nexports.Buffer = Buffer\nexports.SlowBuffer = SlowBuffer\nexports.INSPECT_MAX_BYTES = 50\n\n/**\n * If `Buffer.TYPED_ARRAY_SUPPORT`:\n * === true Use Uint8Array implementation (fastest)\n * === false Use Object implementation (most compatible, even IE6)\n *\n * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,\n * Opera 11.6+, iOS 4.2+.\n *\n * Due to various browser bugs, sometimes the Object implementation will be used even\n * when the browser supports typed arrays.\n *\n * Note:\n *\n * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,\n * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.\n *\n * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.\n *\n * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of\n * incorrect length in some situations.\n\n * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they\n * get the Object implementation, which is slower but behaves correctly.\n */\nBuffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined\n ? global.TYPED_ARRAY_SUPPORT\n : typedArraySupport()\n\n/*\n * Export kMaxLength after typed array support is determined.\n */\nexports.kMaxLength = kMaxLength()\n\nfunction typedArraySupport () {\n try {\n var arr = new Uint8Array(1)\n arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}\n return arr.foo() === 42 && // typed array instances can be augmented\n typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`\n arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`\n } catch (e) {\n return false\n }\n}\n\nfunction kMaxLength () {\n return Buffer.TYPED_ARRAY_SUPPORT\n ? 0x7fffffff\n : 0x3fffffff\n}\n\nfunction createBuffer (that, length) {\n if (kMaxLength() < length) {\n throw new RangeError('Invalid typed array length')\n }\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n // Return an augmented `Uint8Array` instance, for best performance\n that = new Uint8Array(length)\n that.__proto__ = Buffer.prototype\n } else {\n // Fallback: Return an object instance of the Buffer class\n if (that === null) {\n that = new Buffer(length)\n }\n that.length = length\n }\n\n return that\n}\n\n/**\n * The Buffer constructor returns instances of `Uint8Array` that have their\n * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of\n * `Uint8Array`, so the returned instances will have all the node `Buffer` methods\n * and the `Uint8Array` methods. Square bracket notation works as expected -- it\n * returns a single octet.\n *\n * The `Uint8Array` prototype remains unmodified.\n */\n\nfunction Buffer (arg, encodingOrOffset, length) {\n if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {\n return new Buffer(arg, encodingOrOffset, length)\n }\n\n // Common case.\n if (typeof arg === 'number') {\n if (typeof encodingOrOffset === 'string') {\n throw new Error(\n 'If encoding is specified then the first argument must be a string'\n )\n }\n return allocUnsafe(this, arg)\n }\n return from(this, arg, encodingOrOffset, length)\n}\n\nBuffer.poolSize = 8192 // not used by this implementation\n\n// TODO: Legacy, not needed anymore. Remove in next major version.\nBuffer._augment = function (arr) {\n arr.__proto__ = Buffer.prototype\n return arr\n}\n\nfunction from (that, value, encodingOrOffset, length) {\n if (typeof value === 'number') {\n throw new TypeError('\"value\" argument must not be a number')\n }\n\n if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {\n return fromArrayBuffer(that, value, encodingOrOffset, length)\n }\n\n if (typeof value === 'string') {\n return fromString(that, value, encodingOrOffset)\n }\n\n return fromObject(that, value)\n}\n\n/**\n * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError\n * if value is a number.\n * Buffer.from(str[, encoding])\n * Buffer.from(array)\n * Buffer.from(buffer)\n * Buffer.from(arrayBuffer[, byteOffset[, length]])\n **/\nBuffer.from = function (value, encodingOrOffset, length) {\n return from(null, value, encodingOrOffset, length)\n}\n\nif (Buffer.TYPED_ARRAY_SUPPORT) {\n Buffer.prototype.__proto__ = Uint8Array.prototype\n Buffer.__proto__ = Uint8Array\n if (typeof Symbol !== 'undefined' && Symbol.species &&\n Buffer[Symbol.species] === Buffer) {\n // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97\n Object.defineProperty(Buffer, Symbol.species, {\n value: null,\n configurable: true\n })\n }\n}\n\nfunction assertSize (size) {\n if (typeof size !== 'number') {\n throw new TypeError('\"size\" argument must be a number')\n } else if (size < 0) {\n throw new RangeError('\"size\" argument must not be negative')\n }\n}\n\nfunction alloc (that, size, fill, encoding) {\n assertSize(size)\n if (size <= 0) {\n return createBuffer(that, size)\n }\n if (fill !== undefined) {\n // Only pay attention to encoding if it's a string. This\n // prevents accidentally sending in a number that would\n // be interpretted as a start offset.\n return typeof encoding === 'string'\n ? createBuffer(that, size).fill(fill, encoding)\n : createBuffer(that, size).fill(fill)\n }\n return createBuffer(that, size)\n}\n\n/**\n * Creates a new filled Buffer instance.\n * alloc(size[, fill[, encoding]])\n **/\nBuffer.alloc = function (size, fill, encoding) {\n return alloc(null, size, fill, encoding)\n}\n\nfunction allocUnsafe (that, size) {\n assertSize(size)\n that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)\n if (!Buffer.TYPED_ARRAY_SUPPORT) {\n for (var i = 0; i < size; ++i) {\n that[i] = 0\n }\n }\n return that\n}\n\n/**\n * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.\n * */\nBuffer.allocUnsafe = function (size) {\n return allocUnsafe(null, size)\n}\n/**\n * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.\n */\nBuffer.allocUnsafeSlow = function (size) {\n return allocUnsafe(null, size)\n}\n\nfunction fromString (that, string, encoding) {\n if (typeof encoding !== 'string' || encoding === '') {\n encoding = 'utf8'\n }\n\n if (!Buffer.isEncoding(encoding)) {\n throw new TypeError('\"encoding\" must be a valid string encoding')\n }\n\n var length = byteLength(string, encoding) | 0\n that = createBuffer(that, length)\n\n var actual = that.write(string, encoding)\n\n if (actual !== length) {\n // Writing a hex string, for example, that contains invalid characters will\n // cause everything after the first invalid character to be ignored. (e.g.\n // 'abxxcd' will be treated as 'ab')\n that = that.slice(0, actual)\n }\n\n return that\n}\n\nfunction fromArrayLike (that, array) {\n var length = array.length < 0 ? 0 : checked(array.length) | 0\n that = createBuffer(that, length)\n for (var i = 0; i < length; i += 1) {\n that[i] = array[i] & 255\n }\n return that\n}\n\nfunction fromArrayBuffer (that, array, byteOffset, length) {\n array.byteLength // this throws if `array` is not a valid ArrayBuffer\n\n if (byteOffset < 0 || array.byteLength < byteOffset) {\n throw new RangeError('\\'offset\\' is out of bounds')\n }\n\n if (array.byteLength < byteOffset + (length || 0)) {\n throw new RangeError('\\'length\\' is out of bounds')\n }\n\n if (byteOffset === undefined && length === undefined) {\n array = new Uint8Array(array)\n } else if (length === undefined) {\n array = new Uint8Array(array, byteOffset)\n } else {\n array = new Uint8Array(array, byteOffset, length)\n }\n\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n // Return an augmented `Uint8Array` instance, for best performance\n that = array\n that.__proto__ = Buffer.prototype\n } else {\n // Fallback: Return an object instance of the Buffer class\n that = fromArrayLike(that, array)\n }\n return that\n}\n\nfunction fromObject (that, obj) {\n if (Buffer.isBuffer(obj)) {\n var len = checked(obj.length) | 0\n that = createBuffer(that, len)\n\n if (that.length === 0) {\n return that\n }\n\n obj.copy(that, 0, 0, len)\n return that\n }\n\n if (obj) {\n if ((typeof ArrayBuffer !== 'undefined' &&\n obj.buffer instanceof ArrayBuffer) || 'length' in obj) {\n if (typeof obj.length !== 'number' || isnan(obj.length)) {\n return createBuffer(that, 0)\n }\n return fromArrayLike(that, obj)\n }\n\n if (obj.type === 'Buffer' && isArray(obj.data)) {\n return fromArrayLike(that, obj.data)\n }\n }\n\n throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')\n}\n\nfunction checked (length) {\n // Note: cannot use `length < kMaxLength()` here because that fails when\n // length is NaN (which is otherwise coerced to zero.)\n if (length >= kMaxLength()) {\n throw new RangeError('Attempt to allocate Buffer larger than maximum ' +\n 'size: 0x' + kMaxLength().toString(16) + ' bytes')\n }\n return length | 0\n}\n\nfunction SlowBuffer (length) {\n if (+length != length) { // eslint-disable-line eqeqeq\n length = 0\n }\n return Buffer.alloc(+length)\n}\n\nBuffer.isBuffer = function isBuffer (b) {\n return !!(b != null && b._isBuffer)\n}\n\nBuffer.compare = function compare (a, b) {\n if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {\n throw new TypeError('Arguments must be Buffers')\n }\n\n if (a === b) return 0\n\n var x = a.length\n var y = b.length\n\n for (var i = 0, len = Math.min(x, y); i < len; ++i) {\n if (a[i] !== b[i]) {\n x = a[i]\n y = b[i]\n break\n }\n }\n\n if (x < y) return -1\n if (y < x) return 1\n return 0\n}\n\nBuffer.isEncoding = function isEncoding (encoding) {\n switch (String(encoding).toLowerCase()) {\n case 'hex':\n case 'utf8':\n case 'utf-8':\n case 'ascii':\n case 'latin1':\n case 'binary':\n case 'base64':\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return true\n default:\n return false\n }\n}\n\nBuffer.concat = function concat (list, length) {\n if (!isArray(list)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers')\n }\n\n if (list.length === 0) {\n return Buffer.alloc(0)\n }\n\n var i\n if (length === undefined) {\n length = 0\n for (i = 0; i < list.length; ++i) {\n length += list[i].length\n }\n }\n\n var buffer = Buffer.allocUnsafe(length)\n var pos = 0\n for (i = 0; i < list.length; ++i) {\n var buf = list[i]\n if (!Buffer.isBuffer(buf)) {\n throw new TypeError('\"list\" argument must be an Array of Buffers')\n }\n buf.copy(buffer, pos)\n pos += buf.length\n }\n return buffer\n}\n\nfunction byteLength (string, encoding) {\n if (Buffer.isBuffer(string)) {\n return string.length\n }\n if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&\n (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {\n return string.byteLength\n }\n if (typeof string !== 'string') {\n string = '' + string\n }\n\n var len = string.length\n if (len === 0) return 0\n\n // Use a for loop to avoid recursion\n var loweredCase = false\n for (;;) {\n switch (encoding) {\n case 'ascii':\n case 'latin1':\n case 'binary':\n return len\n case 'utf8':\n case 'utf-8':\n case undefined:\n return utf8ToBytes(string).length\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return len * 2\n case 'hex':\n return len >>> 1\n case 'base64':\n return base64ToBytes(string).length\n default:\n if (loweredCase) return utf8ToBytes(string).length // assume utf8\n encoding = ('' + encoding).toLowerCase()\n loweredCase = true\n }\n }\n}\nBuffer.byteLength = byteLength\n\nfunction slowToString (encoding, start, end) {\n var loweredCase = false\n\n // No need to verify that \"this.length <= MAX_UINT32\" since it's a read-only\n // property of a typed array.\n\n // This behaves neither like String nor Uint8Array in that we set start/end\n // to their upper/lower bounds if the value passed is out of range.\n // undefined is handled specially as per ECMA-262 6th Edition,\n // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.\n if (start === undefined || start < 0) {\n start = 0\n }\n // Return early if start > this.length. Done here to prevent potential uint32\n // coercion fail below.\n if (start > this.length) {\n return ''\n }\n\n if (end === undefined || end > this.length) {\n end = this.length\n }\n\n if (end <= 0) {\n return ''\n }\n\n // Force coersion to uint32. This will also coerce falsey/NaN values to 0.\n end >>>= 0\n start >>>= 0\n\n if (end <= start) {\n return ''\n }\n\n if (!encoding) encoding = 'utf8'\n\n while (true) {\n switch (encoding) {\n case 'hex':\n return hexSlice(this, start, end)\n\n case 'utf8':\n case 'utf-8':\n return utf8Slice(this, start, end)\n\n case 'ascii':\n return asciiSlice(this, start, end)\n\n case 'latin1':\n case 'binary':\n return latin1Slice(this, start, end)\n\n case 'base64':\n return base64Slice(this, start, end)\n\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return utf16leSlice(this, start, end)\n\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n encoding = (encoding + '').toLowerCase()\n loweredCase = true\n }\n }\n}\n\n// The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect\n// Buffer instances.\nBuffer.prototype._isBuffer = true\n\nfunction swap (b, n, m) {\n var i = b[n]\n b[n] = b[m]\n b[m] = i\n}\n\nBuffer.prototype.swap16 = function swap16 () {\n var len = this.length\n if (len % 2 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 16-bits')\n }\n for (var i = 0; i < len; i += 2) {\n swap(this, i, i + 1)\n }\n return this\n}\n\nBuffer.prototype.swap32 = function swap32 () {\n var len = this.length\n if (len % 4 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 32-bits')\n }\n for (var i = 0; i < len; i += 4) {\n swap(this, i, i + 3)\n swap(this, i + 1, i + 2)\n }\n return this\n}\n\nBuffer.prototype.swap64 = function swap64 () {\n var len = this.length\n if (len % 8 !== 0) {\n throw new RangeError('Buffer size must be a multiple of 64-bits')\n }\n for (var i = 0; i < len; i += 8) {\n swap(this, i, i + 7)\n swap(this, i + 1, i + 6)\n swap(this, i + 2, i + 5)\n swap(this, i + 3, i + 4)\n }\n return this\n}\n\nBuffer.prototype.toString = function toString () {\n var length = this.length | 0\n if (length === 0) return ''\n if (arguments.length === 0) return utf8Slice(this, 0, length)\n return slowToString.apply(this, arguments)\n}\n\nBuffer.prototype.equals = function equals (b) {\n if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')\n if (this === b) return true\n return Buffer.compare(this, b) === 0\n}\n\nBuffer.prototype.inspect = function inspect () {\n var str = ''\n var max = exports.INSPECT_MAX_BYTES\n if (this.length > 0) {\n str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')\n if (this.length > max) str += ' ... '\n }\n return ''\n}\n\nBuffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {\n if (!Buffer.isBuffer(target)) {\n throw new TypeError('Argument must be a Buffer')\n }\n\n if (start === undefined) {\n start = 0\n }\n if (end === undefined) {\n end = target ? target.length : 0\n }\n if (thisStart === undefined) {\n thisStart = 0\n }\n if (thisEnd === undefined) {\n thisEnd = this.length\n }\n\n if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {\n throw new RangeError('out of range index')\n }\n\n if (thisStart >= thisEnd && start >= end) {\n return 0\n }\n if (thisStart >= thisEnd) {\n return -1\n }\n if (start >= end) {\n return 1\n }\n\n start >>>= 0\n end >>>= 0\n thisStart >>>= 0\n thisEnd >>>= 0\n\n if (this === target) return 0\n\n var x = thisEnd - thisStart\n var y = end - start\n var len = Math.min(x, y)\n\n var thisCopy = this.slice(thisStart, thisEnd)\n var targetCopy = target.slice(start, end)\n\n for (var i = 0; i < len; ++i) {\n if (thisCopy[i] !== targetCopy[i]) {\n x = thisCopy[i]\n y = targetCopy[i]\n break\n }\n }\n\n if (x < y) return -1\n if (y < x) return 1\n return 0\n}\n\n// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,\n// OR the last index of `val` in `buffer` at offset <= `byteOffset`.\n//\n// Arguments:\n// - buffer - a Buffer to search\n// - val - a string, Buffer, or number\n// - byteOffset - an index into `buffer`; will be clamped to an int32\n// - encoding - an optional encoding, relevant is val is a string\n// - dir - true for indexOf, false for lastIndexOf\nfunction bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {\n // Empty buffer means no match\n if (buffer.length === 0) return -1\n\n // Normalize byteOffset\n if (typeof byteOffset === 'string') {\n encoding = byteOffset\n byteOffset = 0\n } else if (byteOffset > 0x7fffffff) {\n byteOffset = 0x7fffffff\n } else if (byteOffset < -0x80000000) {\n byteOffset = -0x80000000\n }\n byteOffset = +byteOffset // Coerce to Number.\n if (isNaN(byteOffset)) {\n // byteOffset: it it's undefined, null, NaN, \"foo\", etc, search whole buffer\n byteOffset = dir ? 0 : (buffer.length - 1)\n }\n\n // Normalize byteOffset: negative offsets start from the end of the buffer\n if (byteOffset < 0) byteOffset = buffer.length + byteOffset\n if (byteOffset >= buffer.length) {\n if (dir) return -1\n else byteOffset = buffer.length - 1\n } else if (byteOffset < 0) {\n if (dir) byteOffset = 0\n else return -1\n }\n\n // Normalize val\n if (typeof val === 'string') {\n val = Buffer.from(val, encoding)\n }\n\n // Finally, search either indexOf (if dir is true) or lastIndexOf\n if (Buffer.isBuffer(val)) {\n // Special case: looking for empty string/buffer always fails\n if (val.length === 0) {\n return -1\n }\n return arrayIndexOf(buffer, val, byteOffset, encoding, dir)\n } else if (typeof val === 'number') {\n val = val & 0xFF // Search for a byte value [0-255]\n if (Buffer.TYPED_ARRAY_SUPPORT &&\n typeof Uint8Array.prototype.indexOf === 'function') {\n if (dir) {\n return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)\n } else {\n return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)\n }\n }\n return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)\n }\n\n throw new TypeError('val must be string, number or Buffer')\n}\n\nfunction arrayIndexOf (arr, val, byteOffset, encoding, dir) {\n var indexSize = 1\n var arrLength = arr.length\n var valLength = val.length\n\n if (encoding !== undefined) {\n encoding = String(encoding).toLowerCase()\n if (encoding === 'ucs2' || encoding === 'ucs-2' ||\n encoding === 'utf16le' || encoding === 'utf-16le') {\n if (arr.length < 2 || val.length < 2) {\n return -1\n }\n indexSize = 2\n arrLength /= 2\n valLength /= 2\n byteOffset /= 2\n }\n }\n\n function read (buf, i) {\n if (indexSize === 1) {\n return buf[i]\n } else {\n return buf.readUInt16BE(i * indexSize)\n }\n }\n\n var i\n if (dir) {\n var foundIndex = -1\n for (i = byteOffset; i < arrLength; i++) {\n if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {\n if (foundIndex === -1) foundIndex = i\n if (i - foundIndex + 1 === valLength) return foundIndex * indexSize\n } else {\n if (foundIndex !== -1) i -= i - foundIndex\n foundIndex = -1\n }\n }\n } else {\n if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength\n for (i = byteOffset; i >= 0; i--) {\n var found = true\n for (var j = 0; j < valLength; j++) {\n if (read(arr, i + j) !== read(val, j)) {\n found = false\n break\n }\n }\n if (found) return i\n }\n }\n\n return -1\n}\n\nBuffer.prototype.includes = function includes (val, byteOffset, encoding) {\n return this.indexOf(val, byteOffset, encoding) !== -1\n}\n\nBuffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, true)\n}\n\nBuffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {\n return bidirectionalIndexOf(this, val, byteOffset, encoding, false)\n}\n\nfunction hexWrite (buf, string, offset, length) {\n offset = Number(offset) || 0\n var remaining = buf.length - offset\n if (!length) {\n length = remaining\n } else {\n length = Number(length)\n if (length > remaining) {\n length = remaining\n }\n }\n\n // must be an even number of digits\n var strLen = string.length\n if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')\n\n if (length > strLen / 2) {\n length = strLen / 2\n }\n for (var i = 0; i < length; ++i) {\n var parsed = parseInt(string.substr(i * 2, 2), 16)\n if (isNaN(parsed)) return i\n buf[offset + i] = parsed\n }\n return i\n}\n\nfunction utf8Write (buf, string, offset, length) {\n return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nfunction asciiWrite (buf, string, offset, length) {\n return blitBuffer(asciiToBytes(string), buf, offset, length)\n}\n\nfunction latin1Write (buf, string, offset, length) {\n return asciiWrite(buf, string, offset, length)\n}\n\nfunction base64Write (buf, string, offset, length) {\n return blitBuffer(base64ToBytes(string), buf, offset, length)\n}\n\nfunction ucs2Write (buf, string, offset, length) {\n return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)\n}\n\nBuffer.prototype.write = function write (string, offset, length, encoding) {\n // Buffer#write(string)\n if (offset === undefined) {\n encoding = 'utf8'\n length = this.length\n offset = 0\n // Buffer#write(string, encoding)\n } else if (length === undefined && typeof offset === 'string') {\n encoding = offset\n length = this.length\n offset = 0\n // Buffer#write(string, offset[, length][, encoding])\n } else if (isFinite(offset)) {\n offset = offset | 0\n if (isFinite(length)) {\n length = length | 0\n if (encoding === undefined) encoding = 'utf8'\n } else {\n encoding = length\n length = undefined\n }\n // legacy write(string, encoding, offset, length) - remove in v0.13\n } else {\n throw new Error(\n 'Buffer.write(string, encoding, offset[, length]) is no longer supported'\n )\n }\n\n var remaining = this.length - offset\n if (length === undefined || length > remaining) length = remaining\n\n if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {\n throw new RangeError('Attempt to write outside buffer bounds')\n }\n\n if (!encoding) encoding = 'utf8'\n\n var loweredCase = false\n for (;;) {\n switch (encoding) {\n case 'hex':\n return hexWrite(this, string, offset, length)\n\n case 'utf8':\n case 'utf-8':\n return utf8Write(this, string, offset, length)\n\n case 'ascii':\n return asciiWrite(this, string, offset, length)\n\n case 'latin1':\n case 'binary':\n return latin1Write(this, string, offset, length)\n\n case 'base64':\n // Warning: maxLength not taken into account in base64Write\n return base64Write(this, string, offset, length)\n\n case 'ucs2':\n case 'ucs-2':\n case 'utf16le':\n case 'utf-16le':\n return ucs2Write(this, string, offset, length)\n\n default:\n if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)\n encoding = ('' + encoding).toLowerCase()\n loweredCase = true\n }\n }\n}\n\nBuffer.prototype.toJSON = function toJSON () {\n return {\n type: 'Buffer',\n data: Array.prototype.slice.call(this._arr || this, 0)\n }\n}\n\nfunction base64Slice (buf, start, end) {\n if (start === 0 && end === buf.length) {\n return base64.fromByteArray(buf)\n } else {\n return base64.fromByteArray(buf.slice(start, end))\n }\n}\n\nfunction utf8Slice (buf, start, end) {\n end = Math.min(buf.length, end)\n var res = []\n\n var i = start\n while (i < end) {\n var firstByte = buf[i]\n var codePoint = null\n var bytesPerSequence = (firstByte > 0xEF) ? 4\n : (firstByte > 0xDF) ? 3\n : (firstByte > 0xBF) ? 2\n : 1\n\n if (i + bytesPerSequence <= end) {\n var secondByte, thirdByte, fourthByte, tempCodePoint\n\n switch (bytesPerSequence) {\n case 1:\n if (firstByte < 0x80) {\n codePoint = firstByte\n }\n break\n case 2:\n secondByte = buf[i + 1]\n if ((secondByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)\n if (tempCodePoint > 0x7F) {\n codePoint = tempCodePoint\n }\n }\n break\n case 3:\n secondByte = buf[i + 1]\n thirdByte = buf[i + 2]\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)\n if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {\n codePoint = tempCodePoint\n }\n }\n break\n case 4:\n secondByte = buf[i + 1]\n thirdByte = buf[i + 2]\n fourthByte = buf[i + 3]\n if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {\n tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)\n if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {\n codePoint = tempCodePoint\n }\n }\n }\n }\n\n if (codePoint === null) {\n // we did not generate a valid codePoint so insert a\n // replacement char (U+FFFD) and advance only 1 byte\n codePoint = 0xFFFD\n bytesPerSequence = 1\n } else if (codePoint > 0xFFFF) {\n // encode to utf16 (surrogate pair dance)\n codePoint -= 0x10000\n res.push(codePoint >>> 10 & 0x3FF | 0xD800)\n codePoint = 0xDC00 | codePoint & 0x3FF\n }\n\n res.push(codePoint)\n i += bytesPerSequence\n }\n\n return decodeCodePointsArray(res)\n}\n\n// Based on http://stackoverflow.com/a/22747272/680742, the browser with\n// the lowest limit is Chrome, with 0x10000 args.\n// We go 1 magnitude less, for safety\nvar MAX_ARGUMENTS_LENGTH = 0x1000\n\nfunction decodeCodePointsArray (codePoints) {\n var len = codePoints.length\n if (len <= MAX_ARGUMENTS_LENGTH) {\n return String.fromCharCode.apply(String, codePoints) // avoid extra slice()\n }\n\n // Decode in chunks to avoid \"call stack size exceeded\".\n var res = ''\n var i = 0\n while (i < len) {\n res += String.fromCharCode.apply(\n String,\n codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)\n )\n }\n return res\n}\n\nfunction asciiSlice (buf, start, end) {\n var ret = ''\n end = Math.min(buf.length, end)\n\n for (var i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i] & 0x7F)\n }\n return ret\n}\n\nfunction latin1Slice (buf, start, end) {\n var ret = ''\n end = Math.min(buf.length, end)\n\n for (var i = start; i < end; ++i) {\n ret += String.fromCharCode(buf[i])\n }\n return ret\n}\n\nfunction hexSlice (buf, start, end) {\n var len = buf.length\n\n if (!start || start < 0) start = 0\n if (!end || end < 0 || end > len) end = len\n\n var out = ''\n for (var i = start; i < end; ++i) {\n out += toHex(buf[i])\n }\n return out\n}\n\nfunction utf16leSlice (buf, start, end) {\n var bytes = buf.slice(start, end)\n var res = ''\n for (var i = 0; i < bytes.length; i += 2) {\n res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)\n }\n return res\n}\n\nBuffer.prototype.slice = function slice (start, end) {\n var len = this.length\n start = ~~start\n end = end === undefined ? len : ~~end\n\n if (start < 0) {\n start += len\n if (start < 0) start = 0\n } else if (start > len) {\n start = len\n }\n\n if (end < 0) {\n end += len\n if (end < 0) end = 0\n } else if (end > len) {\n end = len\n }\n\n if (end < start) end = start\n\n var newBuf\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n newBuf = this.subarray(start, end)\n newBuf.__proto__ = Buffer.prototype\n } else {\n var sliceLen = end - start\n newBuf = new Buffer(sliceLen, undefined)\n for (var i = 0; i < sliceLen; ++i) {\n newBuf[i] = this[i + start]\n }\n }\n\n return newBuf\n}\n\n/*\n * Need to make sure that buffer isn't trying to write out of bounds.\n */\nfunction checkOffset (offset, ext, length) {\n if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')\n if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')\n}\n\nBuffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n var val = this[offset]\n var mul = 1\n var i = 0\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul\n }\n\n return val\n}\n\nBuffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) {\n checkOffset(offset, byteLength, this.length)\n }\n\n var val = this[offset + --byteLength]\n var mul = 1\n while (byteLength > 0 && (mul *= 0x100)) {\n val += this[offset + --byteLength] * mul\n }\n\n return val\n}\n\nBuffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 1, this.length)\n return this[offset]\n}\n\nBuffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n return this[offset] | (this[offset + 1] << 8)\n}\n\nBuffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n return (this[offset] << 8) | this[offset + 1]\n}\n\nBuffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return ((this[offset]) |\n (this[offset + 1] << 8) |\n (this[offset + 2] << 16)) +\n (this[offset + 3] * 0x1000000)\n}\n\nBuffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset] * 0x1000000) +\n ((this[offset + 1] << 16) |\n (this[offset + 2] << 8) |\n this[offset + 3])\n}\n\nBuffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n var val = this[offset]\n var mul = 1\n var i = 0\n while (++i < byteLength && (mul *= 0x100)) {\n val += this[offset + i] * mul\n }\n mul *= 0x80\n\n if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n return val\n}\n\nBuffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) checkOffset(offset, byteLength, this.length)\n\n var i = byteLength\n var mul = 1\n var val = this[offset + --i]\n while (i > 0 && (mul *= 0x100)) {\n val += this[offset + --i] * mul\n }\n mul *= 0x80\n\n if (val >= mul) val -= Math.pow(2, 8 * byteLength)\n\n return val\n}\n\nBuffer.prototype.readInt8 = function readInt8 (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 1, this.length)\n if (!(this[offset] & 0x80)) return (this[offset])\n return ((0xff - this[offset] + 1) * -1)\n}\n\nBuffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n var val = this[offset] | (this[offset + 1] << 8)\n return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 2, this.length)\n var val = this[offset + 1] | (this[offset] << 8)\n return (val & 0x8000) ? val | 0xFFFF0000 : val\n}\n\nBuffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset]) |\n (this[offset + 1] << 8) |\n (this[offset + 2] << 16) |\n (this[offset + 3] << 24)\n}\n\nBuffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n\n return (this[offset] << 24) |\n (this[offset + 1] << 16) |\n (this[offset + 2] << 8) |\n (this[offset + 3])\n}\n\nBuffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n return ieee754.read(this, offset, true, 23, 4)\n}\n\nBuffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 4, this.length)\n return ieee754.read(this, offset, false, 23, 4)\n}\n\nBuffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 8, this.length)\n return ieee754.read(this, offset, true, 52, 8)\n}\n\nBuffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {\n if (!noAssert) checkOffset(offset, 8, this.length)\n return ieee754.read(this, offset, false, 52, 8)\n}\n\nfunction checkInt (buf, value, offset, ext, max, min) {\n if (!Buffer.isBuffer(buf)) throw new TypeError('\"buffer\" argument must be a Buffer instance')\n if (value > max || value < min) throw new RangeError('\"value\" argument is out of bounds')\n if (offset + ext > buf.length) throw new RangeError('Index out of range')\n}\n\nBuffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) {\n var maxBytes = Math.pow(2, 8 * byteLength) - 1\n checkInt(this, value, offset, byteLength, maxBytes, 0)\n }\n\n var mul = 1\n var i = 0\n this[offset] = value & 0xFF\n while (++i < byteLength && (mul *= 0x100)) {\n this[offset + i] = (value / mul) & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n byteLength = byteLength | 0\n if (!noAssert) {\n var maxBytes = Math.pow(2, 8 * byteLength) - 1\n checkInt(this, value, offset, byteLength, maxBytes, 0)\n }\n\n var i = byteLength - 1\n var mul = 1\n this[offset + i] = value & 0xFF\n while (--i >= 0 && (mul *= 0x100)) {\n this[offset + i] = (value / mul) & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)\n if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)\n this[offset] = (value & 0xff)\n return offset + 1\n}\n\nfunction objectWriteUInt16 (buf, value, offset, littleEndian) {\n if (value < 0) value = 0xffff + value + 1\n for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {\n buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>\n (littleEndian ? i : 1 - i) * 8\n }\n}\n\nBuffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n } else {\n objectWriteUInt16(this, value, offset, true)\n }\n return offset + 2\n}\n\nBuffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 8)\n this[offset + 1] = (value & 0xff)\n } else {\n objectWriteUInt16(this, value, offset, false)\n }\n return offset + 2\n}\n\nfunction objectWriteUInt32 (buf, value, offset, littleEndian) {\n if (value < 0) value = 0xffffffff + value + 1\n for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {\n buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff\n }\n}\n\nBuffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset + 3] = (value >>> 24)\n this[offset + 2] = (value >>> 16)\n this[offset + 1] = (value >>> 8)\n this[offset] = (value & 0xff)\n } else {\n objectWriteUInt32(this, value, offset, true)\n }\n return offset + 4\n}\n\nBuffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 24)\n this[offset + 1] = (value >>> 16)\n this[offset + 2] = (value >>> 8)\n this[offset + 3] = (value & 0xff)\n } else {\n objectWriteUInt32(this, value, offset, false)\n }\n return offset + 4\n}\n\nBuffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) {\n var limit = Math.pow(2, 8 * byteLength - 1)\n\n checkInt(this, value, offset, byteLength, limit - 1, -limit)\n }\n\n var i = 0\n var mul = 1\n var sub = 0\n this[offset] = value & 0xFF\n while (++i < byteLength && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {\n sub = 1\n }\n this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) {\n var limit = Math.pow(2, 8 * byteLength - 1)\n\n checkInt(this, value, offset, byteLength, limit - 1, -limit)\n }\n\n var i = byteLength - 1\n var mul = 1\n var sub = 0\n this[offset + i] = value & 0xFF\n while (--i >= 0 && (mul *= 0x100)) {\n if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {\n sub = 1\n }\n this[offset + i] = ((value / mul) >> 0) - sub & 0xFF\n }\n\n return offset + byteLength\n}\n\nBuffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)\n if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)\n if (value < 0) value = 0xff + value + 1\n this[offset] = (value & 0xff)\n return offset + 1\n}\n\nBuffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n } else {\n objectWriteUInt16(this, value, offset, true)\n }\n return offset + 2\n}\n\nBuffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 8)\n this[offset + 1] = (value & 0xff)\n } else {\n objectWriteUInt16(this, value, offset, false)\n }\n return offset + 2\n}\n\nBuffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value & 0xff)\n this[offset + 1] = (value >>> 8)\n this[offset + 2] = (value >>> 16)\n this[offset + 3] = (value >>> 24)\n } else {\n objectWriteUInt32(this, value, offset, true)\n }\n return offset + 4\n}\n\nBuffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {\n value = +value\n offset = offset | 0\n if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)\n if (value < 0) value = 0xffffffff + value + 1\n if (Buffer.TYPED_ARRAY_SUPPORT) {\n this[offset] = (value >>> 24)\n this[offset + 1] = (value >>> 16)\n this[offset + 2] = (value >>> 8)\n this[offset + 3] = (value & 0xff)\n } else {\n objectWriteUInt32(this, value, offset, false)\n }\n return offset + 4\n}\n\nfunction checkIEEE754 (buf, value, offset, ext, max, min) {\n if (offset + ext > buf.length) throw new RangeError('Index out of range')\n if (offset < 0) throw new RangeError('Index out of range')\n}\n\nfunction writeFloat (buf, value, offset, littleEndian, noAssert) {\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)\n }\n ieee754.write(buf, value, offset, littleEndian, 23, 4)\n return offset + 4\n}\n\nBuffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {\n return writeFloat(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {\n return writeFloat(this, value, offset, false, noAssert)\n}\n\nfunction writeDouble (buf, value, offset, littleEndian, noAssert) {\n if (!noAssert) {\n checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)\n }\n ieee754.write(buf, value, offset, littleEndian, 52, 8)\n return offset + 8\n}\n\nBuffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {\n return writeDouble(this, value, offset, true, noAssert)\n}\n\nBuffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {\n return writeDouble(this, value, offset, false, noAssert)\n}\n\n// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)\nBuffer.prototype.copy = function copy (target, targetStart, start, end) {\n if (!start) start = 0\n if (!end && end !== 0) end = this.length\n if (targetStart >= target.length) targetStart = target.length\n if (!targetStart) targetStart = 0\n if (end > 0 && end < start) end = start\n\n // Copy 0 bytes; we're done\n if (end === start) return 0\n if (target.length === 0 || this.length === 0) return 0\n\n // Fatal error conditions\n if (targetStart < 0) {\n throw new RangeError('targetStart out of bounds')\n }\n if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')\n if (end < 0) throw new RangeError('sourceEnd out of bounds')\n\n // Are we oob?\n if (end > this.length) end = this.length\n if (target.length - targetStart < end - start) {\n end = target.length - targetStart + start\n }\n\n var len = end - start\n var i\n\n if (this === target && start < targetStart && targetStart < end) {\n // descending copy from end\n for (i = len - 1; i >= 0; --i) {\n target[i + targetStart] = this[i + start]\n }\n } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {\n // ascending copy from start\n for (i = 0; i < len; ++i) {\n target[i + targetStart] = this[i + start]\n }\n } else {\n Uint8Array.prototype.set.call(\n target,\n this.subarray(start, start + len),\n targetStart\n )\n }\n\n return len\n}\n\n// Usage:\n// buffer.fill(number[, offset[, end]])\n// buffer.fill(buffer[, offset[, end]])\n// buffer.fill(string[, offset[, end]][, encoding])\nBuffer.prototype.fill = function fill (val, start, end, encoding) {\n // Handle string cases:\n if (typeof val === 'string') {\n if (typeof start === 'string') {\n encoding = start\n start = 0\n end = this.length\n } else if (typeof end === 'string') {\n encoding = end\n end = this.length\n }\n if (val.length === 1) {\n var code = val.charCodeAt(0)\n if (code < 256) {\n val = code\n }\n }\n if (encoding !== undefined && typeof encoding !== 'string') {\n throw new TypeError('encoding must be a string')\n }\n if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {\n throw new TypeError('Unknown encoding: ' + encoding)\n }\n } else if (typeof val === 'number') {\n val = val & 255\n }\n\n // Invalid ranges are not set to a default, so can range check early.\n if (start < 0 || this.length < start || this.length < end) {\n throw new RangeError('Out of range index')\n }\n\n if (end <= start) {\n return this\n }\n\n start = start >>> 0\n end = end === undefined ? this.length : end >>> 0\n\n if (!val) val = 0\n\n var i\n if (typeof val === 'number') {\n for (i = start; i < end; ++i) {\n this[i] = val\n }\n } else {\n var bytes = Buffer.isBuffer(val)\n ? val\n : utf8ToBytes(new Buffer(val, encoding).toString())\n var len = bytes.length\n for (i = 0; i < end - start; ++i) {\n this[i + start] = bytes[i % len]\n }\n }\n\n return this\n}\n\n// HELPER FUNCTIONS\n// ================\n\nvar INVALID_BASE64_RE = /[^+\\/0-9A-Za-z-_]/g\n\nfunction base64clean (str) {\n // Node strips out invalid characters like \\n and \\t from the string, base64-js does not\n str = stringtrim(str).replace(INVALID_BASE64_RE, '')\n // Node converts strings with length < 2 to ''\n if (str.length < 2) return ''\n // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not\n while (str.length % 4 !== 0) {\n str = str + '='\n }\n return str\n}\n\nfunction stringtrim (str) {\n if (str.trim) return str.trim()\n return str.replace(/^\\s+|\\s+$/g, '')\n}\n\nfunction toHex (n) {\n if (n < 16) return '0' + n.toString(16)\n return n.toString(16)\n}\n\nfunction utf8ToBytes (string, units) {\n units = units || Infinity\n var codePoint\n var length = string.length\n var leadSurrogate = null\n var bytes = []\n\n for (var i = 0; i < length; ++i) {\n codePoint = string.charCodeAt(i)\n\n // is surrogate component\n if (codePoint > 0xD7FF && codePoint < 0xE000) {\n // last char was a lead\n if (!leadSurrogate) {\n // no lead yet\n if (codePoint > 0xDBFF) {\n // unexpected trail\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n continue\n } else if (i + 1 === length) {\n // unpaired lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n continue\n }\n\n // valid lead\n leadSurrogate = codePoint\n\n continue\n }\n\n // 2 leads in a row\n if (codePoint < 0xDC00) {\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n leadSurrogate = codePoint\n continue\n }\n\n // valid surrogate pair\n codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000\n } else if (leadSurrogate) {\n // valid bmp char, but last char was a lead\n if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)\n }\n\n leadSurrogate = null\n\n // encode utf8\n if (codePoint < 0x80) {\n if ((units -= 1) < 0) break\n bytes.push(codePoint)\n } else if (codePoint < 0x800) {\n if ((units -= 2) < 0) break\n bytes.push(\n codePoint >> 0x6 | 0xC0,\n codePoint & 0x3F | 0x80\n )\n } else if (codePoint < 0x10000) {\n if ((units -= 3) < 0) break\n bytes.push(\n codePoint >> 0xC | 0xE0,\n codePoint >> 0x6 & 0x3F | 0x80,\n codePoint & 0x3F | 0x80\n )\n } else if (codePoint < 0x110000) {\n if ((units -= 4) < 0) break\n bytes.push(\n codePoint >> 0x12 | 0xF0,\n codePoint >> 0xC & 0x3F | 0x80,\n codePoint >> 0x6 & 0x3F | 0x80,\n codePoint & 0x3F | 0x80\n )\n } else {\n throw new Error('Invalid code point')\n }\n }\n\n return bytes\n}\n\nfunction asciiToBytes (str) {\n var byteArray = []\n for (var i = 0; i < str.length; ++i) {\n // Node's code seems to be doing this and not & 0x7F..\n byteArray.push(str.charCodeAt(i) & 0xFF)\n }\n return byteArray\n}\n\nfunction utf16leToBytes (str, units) {\n var c, hi, lo\n var byteArray = []\n for (var i = 0; i < str.length; ++i) {\n if ((units -= 2) < 0) break\n\n c = str.charCodeAt(i)\n hi = c >> 8\n lo = c % 256\n byteArray.push(lo)\n byteArray.push(hi)\n }\n\n return byteArray\n}\n\nfunction base64ToBytes (str) {\n return base64.toByteArray(base64clean(str))\n}\n\nfunction blitBuffer (src, dst, offset, length) {\n for (var i = 0; i < length; ++i) {\n if ((i + offset >= dst.length) || (i >= src.length)) break\n dst[i + offset] = src[i]\n }\n return i\n}\n\nfunction isnan (val) {\n return val !== val // eslint-disable-line no-self-compare\n}\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ \"./node_modules/webpack/buildin/global.js\")))\n\n//# sourceURL=webpack:///./node_modules/buffer/index.js?"); /***/ }), /***/ "./node_modules/events/events.js": /*!***************************************!*\ !*** ./node_modules/events/events.js ***! \***************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission is hereby granted, free of charge, to any person obtaining a\n// copy of this software and associated documentation files (the\n// \"Software\"), to deal in the Software without restriction, including\n// without limitation the rights to use, copy, modify, merge, publish,\n// distribute, sublicense, and/or sell copies of the Software, and to permit\n// persons to whom the Software is furnished to do so, subject to the\n// following conditions:\n//\n// The above copyright notice and this permission notice shall be included\n// in all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN\n// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,\n// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR\n// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE\n// USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n\nvar R = typeof Reflect === 'object' ? Reflect : null\nvar ReflectApply = R && typeof R.apply === 'function'\n ? R.apply\n : function ReflectApply(target, receiver, args) {\n return Function.prototype.apply.call(target, receiver, args);\n }\n\nvar ReflectOwnKeys\nif (R && typeof R.ownKeys === 'function') {\n ReflectOwnKeys = R.ownKeys\n} else if (Object.getOwnPropertySymbols) {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target)\n .concat(Object.getOwnPropertySymbols(target));\n };\n} else {\n ReflectOwnKeys = function ReflectOwnKeys(target) {\n return Object.getOwnPropertyNames(target);\n };\n}\n\nfunction ProcessEmitWarning(warning) {\n if (console && console.warn) console.warn(warning);\n}\n\nvar NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {\n return value !== value;\n}\n\nfunction EventEmitter() {\n EventEmitter.init.call(this);\n}\nmodule.exports = EventEmitter;\n\n// Backwards-compat with node 0.10.x\nEventEmitter.EventEmitter = EventEmitter;\n\nEventEmitter.prototype._events = undefined;\nEventEmitter.prototype._eventsCount = 0;\nEventEmitter.prototype._maxListeners = undefined;\n\n// By default EventEmitters will print a warning if more than 10 listeners are\n// added to it. This is a useful default which helps finding memory leaks.\nvar defaultMaxListeners = 10;\n\nObject.defineProperty(EventEmitter, 'defaultMaxListeners', {\n enumerable: true,\n get: function() {\n return defaultMaxListeners;\n },\n set: function(arg) {\n if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {\n throw new RangeError('The value of \"defaultMaxListeners\" is out of range. It must be a non-negative number. Received ' + arg + '.');\n }\n defaultMaxListeners = arg;\n }\n});\n\nEventEmitter.init = function() {\n\n if (this._events === undefined ||\n this._events === Object.getPrototypeOf(this)._events) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n }\n\n this._maxListeners = this._maxListeners || undefined;\n};\n\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nEventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {\n if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {\n throw new RangeError('The value of \"n\" is out of range. It must be a non-negative number. Received ' + n + '.');\n }\n this._maxListeners = n;\n return this;\n};\n\nfunction $getMaxListeners(that) {\n if (that._maxListeners === undefined)\n return EventEmitter.defaultMaxListeners;\n return that._maxListeners;\n}\n\nEventEmitter.prototype.getMaxListeners = function getMaxListeners() {\n return $getMaxListeners(this);\n};\n\nEventEmitter.prototype.emit = function emit(type) {\n var args = [];\n for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);\n var doError = (type === 'error');\n\n var events = this._events;\n if (events !== undefined)\n doError = (doError && events.error === undefined);\n else if (!doError)\n return false;\n\n // If there is no 'error' event listener then throw.\n if (doError) {\n var er;\n if (args.length > 0)\n er = args[0];\n if (er instanceof Error) {\n // Note: The comments on the `throw` lines are intentional, they show\n // up in Node's output if this results in an unhandled exception.\n throw er; // Unhandled 'error' event\n }\n // At least give some kind of context to the user\n var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));\n err.context = er;\n throw err; // Unhandled 'error' event\n }\n\n var handler = events[type];\n\n if (handler === undefined)\n return false;\n\n if (typeof handler === 'function') {\n ReflectApply(handler, this, args);\n } else {\n var len = handler.length;\n var listeners = arrayClone(handler, len);\n for (var i = 0; i < len; ++i)\n ReflectApply(listeners[i], this, args);\n }\n\n return true;\n};\n\nfunction _addListener(target, type, listener, prepend) {\n var m;\n var events;\n var existing;\n\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n\n events = target._events;\n if (events === undefined) {\n events = target._events = Object.create(null);\n target._eventsCount = 0;\n } else {\n // To avoid recursion in the case that type === \"newListener\"! Before\n // adding it to the listeners, first emit \"newListener\".\n if (events.newListener !== undefined) {\n target.emit('newListener', type,\n listener.listener ? listener.listener : listener);\n\n // Re-assign `events` because a newListener handler could have caused the\n // this._events to be assigned to a new object\n events = target._events;\n }\n existing = events[type];\n }\n\n if (existing === undefined) {\n // Optimize the case of one listener. Don't need the extra array object.\n existing = events[type] = listener;\n ++target._eventsCount;\n } else {\n if (typeof existing === 'function') {\n // Adding the second element, need to change to array.\n existing = events[type] =\n prepend ? [listener, existing] : [existing, listener];\n // If we've already got an array, just append.\n } else if (prepend) {\n existing.unshift(listener);\n } else {\n existing.push(listener);\n }\n\n // Check for listener leak\n m = $getMaxListeners(target);\n if (m > 0 && existing.length > m && !existing.warned) {\n existing.warned = true;\n // No error code for this since it is a Warning\n // eslint-disable-next-line no-restricted-syntax\n var w = new Error('Possible EventEmitter memory leak detected. ' +\n existing.length + ' ' + String(type) + ' listeners ' +\n 'added. Use emitter.setMaxListeners() to ' +\n 'increase limit');\n w.name = 'MaxListenersExceededWarning';\n w.emitter = target;\n w.type = type;\n w.count = existing.length;\n ProcessEmitWarning(w);\n }\n }\n\n return target;\n}\n\nEventEmitter.prototype.addListener = function addListener(type, listener) {\n return _addListener(this, type, listener, false);\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.prependListener =\n function prependListener(type, listener) {\n return _addListener(this, type, listener, true);\n };\n\nfunction onceWrapper() {\n var args = [];\n for (var i = 0; i < arguments.length; i++) args.push(arguments[i]);\n if (!this.fired) {\n this.target.removeListener(this.type, this.wrapFn);\n this.fired = true;\n ReflectApply(this.listener, this.target, args);\n }\n}\n\nfunction _onceWrap(target, type, listener) {\n var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };\n var wrapped = onceWrapper.bind(state);\n wrapped.listener = listener;\n state.wrapFn = wrapped;\n return wrapped;\n}\n\nEventEmitter.prototype.once = function once(type, listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n this.on(type, _onceWrap(this, type, listener));\n return this;\n};\n\nEventEmitter.prototype.prependOnceListener =\n function prependOnceListener(type, listener) {\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n this.prependListener(type, _onceWrap(this, type, listener));\n return this;\n };\n\n// Emits a 'removeListener' event if and only if the listener was removed.\nEventEmitter.prototype.removeListener =\n function removeListener(type, listener) {\n var list, events, position, i, originalListener;\n\n if (typeof listener !== 'function') {\n throw new TypeError('The \"listener\" argument must be of type Function. Received type ' + typeof listener);\n }\n\n events = this._events;\n if (events === undefined)\n return this;\n\n list = events[type];\n if (list === undefined)\n return this;\n\n if (list === listener || list.listener === listener) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else {\n delete events[type];\n if (events.removeListener)\n this.emit('removeListener', type, list.listener || listener);\n }\n } else if (typeof list !== 'function') {\n position = -1;\n\n for (i = list.length - 1; i >= 0; i--) {\n if (list[i] === listener || list[i].listener === listener) {\n originalListener = list[i].listener;\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (position === 0)\n list.shift();\n else {\n spliceOne(list, position);\n }\n\n if (list.length === 1)\n events[type] = list[0];\n\n if (events.removeListener !== undefined)\n this.emit('removeListener', type, originalListener || listener);\n }\n\n return this;\n };\n\nEventEmitter.prototype.off = EventEmitter.prototype.removeListener;\n\nEventEmitter.prototype.removeAllListeners =\n function removeAllListeners(type) {\n var listeners, events, i;\n\n events = this._events;\n if (events === undefined)\n return this;\n\n // not listening for removeListener, no need to emit\n if (events.removeListener === undefined) {\n if (arguments.length === 0) {\n this._events = Object.create(null);\n this._eventsCount = 0;\n } else if (events[type] !== undefined) {\n if (--this._eventsCount === 0)\n this._events = Object.create(null);\n else\n delete events[type];\n }\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n var keys = Object.keys(events);\n var key;\n for (i = 0; i < keys.length; ++i) {\n key = keys[i];\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = Object.create(null);\n this._eventsCount = 0;\n return this;\n }\n\n listeners = events[type];\n\n if (typeof listeners === 'function') {\n this.removeListener(type, listeners);\n } else if (listeners !== undefined) {\n // LIFO order\n for (i = listeners.length - 1; i >= 0; i--) {\n this.removeListener(type, listeners[i]);\n }\n }\n\n return this;\n };\n\nfunction _listeners(target, type, unwrap) {\n var events = target._events;\n\n if (events === undefined)\n return [];\n\n var evlistener = events[type];\n if (evlistener === undefined)\n return [];\n\n if (typeof evlistener === 'function')\n return unwrap ? [evlistener.listener || evlistener] : [evlistener];\n\n return unwrap ?\n unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);\n}\n\nEventEmitter.prototype.listeners = function listeners(type) {\n return _listeners(this, type, true);\n};\n\nEventEmitter.prototype.rawListeners = function rawListeners(type) {\n return _listeners(this, type, false);\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n if (typeof emitter.listenerCount === 'function') {\n return emitter.listenerCount(type);\n } else {\n return listenerCount.call(emitter, type);\n }\n};\n\nEventEmitter.prototype.listenerCount = listenerCount;\nfunction listenerCount(type) {\n var events = this._events;\n\n if (events !== undefined) {\n var evlistener = events[type];\n\n if (typeof evlistener === 'function') {\n return 1;\n } else if (evlistener !== undefined) {\n return evlistener.length;\n }\n }\n\n return 0;\n}\n\nEventEmitter.prototype.eventNames = function eventNames() {\n return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];\n};\n\nfunction arrayClone(arr, n) {\n var copy = new Array(n);\n for (var i = 0; i < n; ++i)\n copy[i] = arr[i];\n return copy;\n}\n\nfunction spliceOne(list, index) {\n for (; index + 1 < list.length; index++)\n list[index] = list[index + 1];\n list.pop();\n}\n\nfunction unwrapListeners(arr) {\n var ret = new Array(arr.length);\n for (var i = 0; i < ret.length; ++i) {\n ret[i] = arr[i].listener || arr[i];\n }\n return ret;\n}\n\n\n//# sourceURL=webpack:///./node_modules/events/events.js?"); /***/ }), /***/ "./node_modules/ieee754/index.js": /*!***************************************!*\ !*** ./node_modules/ieee754/index.js ***! \***************************************/ /*! no static exports found */ /***/ (function(module, exports) { eval("exports.read = function (buffer, offset, isLE, mLen, nBytes) {\n var e, m\n var eLen = (nBytes * 8) - mLen - 1\n var eMax = (1 << eLen) - 1\n var eBias = eMax >> 1\n var nBits = -7\n var i = isLE ? (nBytes - 1) : 0\n var d = isLE ? -1 : 1\n var s = buffer[offset + i]\n\n i += d\n\n e = s & ((1 << (-nBits)) - 1)\n s >>= (-nBits)\n nBits += eLen\n for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}\n\n m = e & ((1 << (-nBits)) - 1)\n e >>= (-nBits)\n nBits += mLen\n for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}\n\n if (e === 0) {\n e = 1 - eBias\n } else if (e === eMax) {\n return m ? NaN : ((s ? -1 : 1) * Infinity)\n } else {\n m = m + Math.pow(2, mLen)\n e = e - eBias\n }\n return (s ? -1 : 1) * m * Math.pow(2, e - mLen)\n}\n\nexports.write = function (buffer, value, offset, isLE, mLen, nBytes) {\n var e, m, c\n var eLen = (nBytes * 8) - mLen - 1\n var eMax = (1 << eLen) - 1\n var eBias = eMax >> 1\n var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)\n var i = isLE ? 0 : (nBytes - 1)\n var d = isLE ? 1 : -1\n var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0\n\n value = Math.abs(value)\n\n if (isNaN(value) || value === Infinity) {\n m = isNaN(value) ? 1 : 0\n e = eMax\n } else {\n e = Math.floor(Math.log(value) / Math.LN2)\n if (value * (c = Math.pow(2, -e)) < 1) {\n e--\n c *= 2\n }\n if (e + eBias >= 1) {\n value += rt / c\n } else {\n value += rt * Math.pow(2, 1 - eBias)\n }\n if (value * c >= 2) {\n e++\n c /= 2\n }\n\n if (e + eBias >= eMax) {\n m = 0\n e = eMax\n } else if (e + eBias >= 1) {\n m = ((value * c) - 1) * Math.pow(2, mLen)\n e = e + eBias\n } else {\n m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)\n e = 0\n }\n }\n\n for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}\n\n e = (e << mLen) | m\n eLen += mLen\n for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}\n\n buffer[offset + i - d] |= s * 128\n}\n\n\n//# sourceURL=webpack:///./node_modules/ieee754/index.js?"); /***/ }), /***/ "./node_modules/isarray/index.js": /*!***************************************!*\ !*** ./node_modules/isarray/index.js ***! \***************************************/ /*! no static exports found */ /***/ (function(module, exports) { eval("var toString = {}.toString;\n\nmodule.exports = Array.isArray || function (arr) {\n return toString.call(arr) == '[object Array]';\n};\n\n\n//# sourceURL=webpack:///./node_modules/isarray/index.js?"); /***/ }), /***/ "./node_modules/webpack/buildin/global.js": /*!***********************************!*\ !*** (webpack)/buildin/global.js ***! \***********************************/ /*! no static exports found */ /***/ (function(module, exports) { eval("var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n\n\n//# sourceURL=webpack:///(webpack)/buildin/global.js?"); /***/ }), /***/ "./src/api/application/application.ts": /*!********************************************!*\ !*** ./src/api/application/application.ts ***! \********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst base_1 = __webpack_require__(/*! ../base */ \"./src/api/base.ts\");\r\nconst window_1 = __webpack_require__(/*! ../window/window */ \"./src/api/window/window.ts\");\r\nclass NavigationRejectedReply extends base_1.Reply {\r\n}\r\nexports.NavigationRejectedReply = NavigationRejectedReply;\r\n/**\r\n * @typedef {object} Application~options\r\n * @summary Application creation options.\r\n * @desc This is the options object required by {@link Application.create Application.create}.\r\n *\r\n * The following options are required:\r\n * * `uuid` is required in the app manifest as well as by {@link Application.create Application.create}\r\n * * `name` is optional in the app manifest but required by {@link Application.create Application.create}\r\n * * `url` is optional in both the app manifest {@link Application.create Application.create} and but is usually given\r\n * (defaults to `\"about:blank\"` when omitted).\r\n *\r\n * _This jsdoc typedef mirrors the `ApplicationOptions` TypeScript interface in `@types/openfin`._\r\n *\r\n * **IMPORTANT NOTE:**\r\n * This object inherits all the properties of the window creation {@link Window~options options} object,\r\n * which will take priority over those of the same name that may be provided in `mainWindowOptions`.\r\n *\r\n * @property {boolean} [disableIabSecureLogging=false]\r\n * When set to `true` it will disable IAB secure logging for the app.\r\n *\r\n * @property {string} [loadErrorMessage=\"There was an error loading the application.\"]\r\n * An error message to display when the application (launched via manifest) fails to load.\r\n * A dialog box will be launched with the error message just before the runtime exits.\r\n * Load fails such as failed DNS resolutions or aborted connections as well as cancellations, _e.g.,_ `window.stop()`,\r\n * will trigger this dialog.\r\n * Client response codes such as `404 Not Found` are not treated as fails as they are valid server responses.\r\n *\r\n * @property {Window~options} [mainWindowOptions]\r\n * The options of the main window of the application.\r\n * For a description of these options, click the link (in the Type column).\r\n *\r\n * @property {string} [name]\r\n * The name of the application (and the application's main window).\r\n *\r\n * If provided, _must_ match `uuid`.\r\n *\r\n * @property {boolean} [nonPersistent=false]\r\n * A flag to configure the application as non-persistent.\r\n * Runtime exits when there are no persistent apps running.\r\n *\r\n * @property {boolean} [plugins=false]\r\n * Enable Flash at the application level.\r\n *\r\n * @property {boolean} [spellCheck=false]\r\n * Enable spell check at the application level.\r\n *\r\n * @property {string} [url=\"about:blank\"]\r\n * The url to the application (specifically the application's main window).\r\n *\r\n * @property {string} uuid\r\n * The _Unique Universal Identifier_ (UUID) of the application, unique within the set of all other applications\r\n * running in the OpenFin Runtime.\r\n *\r\n * Note that `name` and `uuid` must match.\r\n *\r\n * @property {boolean} [webSecurity=true]\r\n * When set to `false` it will disable the same-origin policy for the app.\r\n */\r\n/**\r\n * @lends Application\r\n */\r\nclass ApplicationModule extends base_1.Base {\r\n /**\r\n * Asynchronously returns an Application object that represents an existing application.\r\n * @param { Identity } identity\r\n * @return {Promise.}\r\n * @tutorial Application.wrap\r\n * @static\r\n */\r\n wrap(identity) {\r\n return Promise.resolve(new Application(this.wire, identity));\r\n }\r\n /**\r\n * Synchronously returns an Application object that represents an existing application.\r\n * @param { Identity } identity\r\n * @return {Application}\r\n * @tutorial Application.wrapSync\r\n * @static\r\n */\r\n wrapSync(identity) {\r\n return new Application(this.wire, identity);\r\n }\r\n // tslint:disable-next-line:function-name\r\n async _create(appOptions) {\r\n await this.wire.sendAction('create-application', appOptions);\r\n return await this.wrap({ uuid: appOptions.uuid });\r\n }\r\n create(appOptions) {\r\n console.warn('Deprecation Warning: fin.Application.create is deprecated. Please use fin.Application.start');\r\n return this._create(appOptions);\r\n }\r\n /**\r\n * Creates and starts a new Application.\r\n * @param { ApplicationOption } appOptions\r\n * @return {Promise.}\r\n * @tutorial Application.start\r\n * @static\r\n */\r\n async start(appOptions) {\r\n const app = await this._create(appOptions);\r\n await this.wire.sendAction('run-application', { uuid: appOptions.uuid });\r\n return app;\r\n }\r\n /**\r\n * Asynchronously returns an Application object that represents the current application\r\n * @return {Promise.}\r\n * @tutorial Application.getCurrent\r\n * @static\r\n */\r\n getCurrent() {\r\n return this.wrap({ uuid: this.wire.me.uuid });\r\n }\r\n /**\r\n * Synchronously returns an Application object that represents the current application\r\n * @return {Application}\r\n * @tutorial Application.getCurrentSync\r\n * @static\r\n */\r\n getCurrentSync() {\r\n return this.wrapSync({ uuid: this.wire.me.uuid });\r\n }\r\n /**\r\n * Retrieves application's manifest and returns a running instance of the application.\r\n * @param {string} manifestUrl - The URL of app's manifest.\r\n * @return {Promise.}\r\n * @tutorial Application.startFromManifest\r\n * @static\r\n */\r\n async startFromManifest(manifestUrl) {\r\n const app = await this._createFromManifest(manifestUrl);\r\n //@ts-ignore using private method without warning.\r\n await app._run();\r\n return app;\r\n }\r\n createFromManifest(manifestUrl) {\r\n console.warn('Deprecation Warning: fin.Application.createFromManifest is deprecated. Please use fin.Application.startFromManifest');\r\n return this._createFromManifest(manifestUrl);\r\n }\r\n // tslint:disable-next-line:function-name\r\n _createFromManifest(manifestUrl) {\r\n return this.wire.sendAction('get-application-manifest', { manifestUrl })\r\n .then(({ payload }) => this.wrap({ uuid: payload.data.startup_app.uuid })\r\n .then(app => {\r\n app._manifestUrl = manifestUrl;\r\n return app;\r\n }));\r\n }\r\n}\r\nexports.default = ApplicationModule;\r\n/**\r\n * @classdesc An object representing an application. Allows the developer to create,\r\n * execute, show/close an application as well as listen to application events.\r\n * @class\r\n * @hideconstructor\r\n */\r\nclass Application extends base_1.EmitterBase {\r\n constructor(wire, identity) {\r\n super(wire, ['application', identity.uuid]);\r\n this.identity = identity;\r\n this.window = new window_1._Window(this.wire, {\r\n uuid: this.identity.uuid,\r\n name: this.identity.uuid\r\n });\r\n }\r\n windowListFromIdentityList(identityList) {\r\n const windowList = [];\r\n identityList.forEach(identity => {\r\n windowList.push(new window_1._Window(this.wire, {\r\n uuid: identity.uuid,\r\n name: identity.name\r\n }));\r\n });\r\n return windowList;\r\n }\r\n /**\r\n * Adds a listener to the end of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - Called whenever an event of the specified type occurs.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function addListener\r\n * @memberof Application\r\n * @instance\r\n * @tutorial Application.EventEmitter\r\n */\r\n /**\r\n * Adds a listener to the end of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - Called whenever an event of the specified type occurs.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function on\r\n * @memberof Application\r\n * @instance\r\n * @tutorial Application.EventEmitter\r\n */\r\n /**\r\n * Adds a one time listener for the event. The listener is invoked only the first time the event is fired, after which it is removed.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function once\r\n * @memberof Application\r\n * @instance\r\n * @tutorial Application.EventEmitter\r\n */\r\n /**\r\n * Adds a listener to the beginning of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function prependListener\r\n * @memberof Application\r\n * @instance\r\n * @tutorial Application.EventEmitter\r\n */\r\n /**\r\n * Adds a one time listener for the event. The listener is invoked only the first time the event is fired, after which it is removed.\r\n * The listener is added to the beginning of the listeners array.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function prependOnceListener\r\n * @memberof Application\r\n * @instance\r\n * @tutorial Application.EventEmitter\r\n */\r\n /**\r\n * Remove a listener from the listener array for the specified event.\r\n * Caution: Calling this method changes the array indices in the listener array behind the listener.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function removeListener\r\n * @memberof Application\r\n * @instance\r\n * @tutorial Application.EventEmitter\r\n */\r\n /**\r\n * Removes all listeners, or those of the specified event.\r\n * @param { string | symbol } [eventType] - The type of the event.\r\n * @return {Promise.}\r\n * @function removeAllListeners\r\n * @memberof Application\r\n * @instance\r\n * @tutorial Application.EventEmitter\r\n */\r\n /**\r\n * Determines if the application is currently running.\r\n * @return {Promise.}\r\n * @tutorial Application.isRunning\r\n */\r\n isRunning() {\r\n return this.wire.sendAction('is-application-running', this.identity)\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Closes the application and any child windows created by the application.\r\n * Cleans the application from state so it is no longer found in getAllApplications.\r\n * @param { boolean } [force = false] Close will be prevented from closing when force is false and\r\n * â€close-requested’ has been subscribed to for application’s main window.\r\n * @return {Promise.}\r\n * @tutorial Application.quit\r\n */\r\n async quit(force = false) {\r\n await this._close(force);\r\n await this.wire.sendAction('destroy-application', Object.assign({ force }, this.identity));\r\n }\r\n //tslint:disable-next-line:function-name\r\n _close(force = false) {\r\n return this.wire.sendAction('close-application', Object.assign({}, this.identity, { force })).then(() => undefined);\r\n }\r\n close(force = false) {\r\n console.warn('Deprecation Warning: Application.close is deprecated Please use Application.quit');\r\n return this._close(force);\r\n }\r\n /**\r\n * Retrieves an array of wrapped fin.Windows for each of the application’s child windows.\r\n * @return {Promise.Array.<_Window>}\r\n * @tutorial Application.getChildWindows\r\n */\r\n getChildWindows() {\r\n return this.wire.sendAction('get-child-windows', this.identity)\r\n .then(({ payload }) => {\r\n const identityList = [];\r\n payload.data.forEach((winName) => {\r\n identityList.push({ uuid: this.identity.uuid, name: winName });\r\n });\r\n return this.windowListFromIdentityList(identityList);\r\n });\r\n }\r\n /**\r\n * Retrieves an array of active window groups for all of the application's windows. Each group is\r\n * represented as an array of wrapped fin.Windows.\r\n * @return {Promise.Array.Array.<_Window>}\r\n * @tutorial Application.getGroups\r\n */\r\n getGroups() {\r\n const winGroups = [];\r\n return this.wire.sendAction('get-application-groups', Object.assign({}, this.identity, {\r\n crossApp: true // cross app group supported\r\n })).then(({ payload }) => {\r\n payload.data.forEach((windowList, index) => {\r\n const identityList = [];\r\n windowList.forEach(winInfo => {\r\n identityList.push({ uuid: winInfo.uuid, name: winInfo.windowName });\r\n });\r\n winGroups[index] = this.windowListFromIdentityList(identityList);\r\n });\r\n return winGroups;\r\n });\r\n }\r\n /**\r\n * Retrieves the JSON manifest that was used to create the application. Invokes the error callback\r\n * if the application was not created from a manifest.\r\n * @return {Promise.}\r\n * @tutorial Application.getManifest\r\n */\r\n getManifest() {\r\n return this.wire.sendAction('get-application-manifest', this.identity)\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves UUID of the application that launches this application. Invokes the error callback\r\n * if the application was created from a manifest.\r\n * @return {Promise.}\r\n * @tutorial Application.getParentUuid\r\n */\r\n getParentUuid() {\r\n return this.wire.sendAction('get-parent-application', this.identity)\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves current application's shortcut configuration.\r\n * @return {Promise.}\r\n * @tutorial Application.getShortcuts\r\n */\r\n getShortcuts() {\r\n return this.wire.sendAction('get-shortcuts', this.identity)\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Returns the current zoom level of the application.\r\n * @return {Promise.}\r\n * @tutorial Application.getZoomLevel\r\n */\r\n getZoomLevel() {\r\n return this.wire.sendAction('get-application-zoom-level', this.identity).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Returns an instance of the main Window of the application\r\n * @return {Promise.<_Window>}\r\n * @tutorial Application.getWindow\r\n */\r\n getWindow() {\r\n return Promise.resolve(this.window);\r\n }\r\n /**\r\n * Manually registers a user with the licensing service. The only data sent by this call is userName and appName.\r\n * @param { string } userName - username to be passed to the RVM.\r\n * @param { string } appName - app name to be passed to the RVM.\r\n * @return {Promise.}\r\n * @tutorial Application.registerUser\r\n */\r\n registerUser(userName, appName) {\r\n return this.wire.sendAction('register-user', Object.assign({}, this.identity, { userName, appName })).then(() => undefined);\r\n }\r\n /**\r\n * Removes the application’s icon from the tray.\r\n * @return {Promise.}\r\n * @tutorial Application.removeTrayIcon\r\n */\r\n removeTrayIcon() {\r\n return this.wire.sendAction('remove-tray-icon', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Restarts the application.\r\n * @return {Promise.}\r\n * @tutorial Application.restart\r\n */\r\n restart() {\r\n return this.wire.sendAction('restart-application', this.identity).then(() => undefined);\r\n }\r\n run() {\r\n console.warn('Deprecation Warning: Application.run is deprecated Please use fin.Application.start');\r\n return this._run();\r\n }\r\n // tslint:disable-next-line:function-name\r\n _run() {\r\n return this.wire.sendAction('run-application', Object.assign({}, this.identity, {\r\n manifestUrl: this._manifestUrl\r\n })).then(() => undefined);\r\n }\r\n /**\r\n * Instructs the RVM to schedule one restart of the application.\r\n * @return {Promise.}\r\n * @tutorial Application.scheduleRestart\r\n */\r\n scheduleRestart() {\r\n return this.wire.sendAction('relaunch-on-close', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Sends a message to the RVM to upload the application's logs. On success,\r\n * an object containing logId is returned.\r\n * @return {Promise.}\r\n * @tutorial Application.sendApplicationLog\r\n */\r\n async sendApplicationLog() {\r\n const { payload } = await this.wire.sendAction('send-application-log', this.identity);\r\n return payload.data;\r\n }\r\n /**\r\n * Adds a customizable icon in the system tray. To listen for a click on the icon use the `tray-icon-clicked` event.\r\n * @param { string } iconUrl Image URL to be used as the icon\r\n * @return {Promise.}\r\n * @tutorial Application.setTrayIcon\r\n */\r\n setTrayIcon(iconUrl) {\r\n return this.wire.sendAction('set-tray-icon', Object.assign({}, this.identity, {\r\n enabledIcon: iconUrl\r\n })).then(() => undefined);\r\n }\r\n /**\r\n * Sets new application's shortcut configuration.\r\n * @param { ShortCutConfig } config New application's shortcut configuration.\r\n * @param { boolean } [config.desktop] - Enable/disable desktop shortcut.\r\n * @param { boolean } [config.startMenu] - Enable/disable start menu shortcut.\r\n * @param { boolean } [config.systemStartup] - Enable/disable system startup shortcut.\r\n * @return {Promise.}\r\n * @tutorial Application.setShortcuts\r\n */\r\n setShortcuts(config) {\r\n return this.wire.sendAction('set-shortcuts', Object.assign({}, this.identity, { data: config })).then(() => undefined);\r\n }\r\n /**\r\n * Sets the zoom level of the application. The original size is 0 and each increment above or below represents zooming 20%\r\n * larger or smaller to default limits of 300% and 50% of original size, respectively.\r\n * @param { number } level The zoom level\r\n * @return {Promise.}\r\n * @tutorial Application.setZoomLevel\r\n */\r\n setZoomLevel(level) {\r\n return this.wire.sendAction('set-application-zoom-level', Object.assign({}, this.identity, { level })).then(() => undefined);\r\n }\r\n /**\r\n * Sets a username to correlate with App Log Management.\r\n * @param { string } username Username to correlate with App's Log.\r\n * @return {Promise.}\r\n * @tutorial Application.setAppLogUsername\r\n */\r\n async setAppLogUsername(username) {\r\n await this.wire.sendAction('set-app-log-username', Object.assign({ data: username }, this.identity));\r\n }\r\n /**\r\n * @summary Retrieves information about the system tray.\r\n * @desc The only information currently returned is the position and dimensions.\r\n * @return {Promise.}\r\n * @tutorial Application.getTrayIconInfo\r\n */\r\n getTrayIconInfo() {\r\n return this.wire.sendAction('get-tray-icon-info', this.identity)\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Closes the application by terminating its process.\r\n * @return {Promise.}\r\n * @tutorial Application.terminate\r\n */\r\n terminate() {\r\n return this.wire.sendAction('terminate-application', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Waits for a hanging application. This method can be called in response to an application\r\n * \"not-responding\" to allow the application to continue and to generate another \"not-responding\"\r\n * message after a certain period of time.\r\n * @return {Promise.}\r\n * @ignore\r\n */\r\n wait() {\r\n return this.wire.sendAction('wait-for-hung-application', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Retrieves information about the application.\r\n * @return {Promise.}\r\n * @tutorial Application.getInfo\r\n */\r\n getInfo() {\r\n return this.wire.sendAction('get-info', this.identity).then(({ payload }) => payload.data);\r\n }\r\n}\r\nexports.Application = Application;\r\n\n\n//# sourceURL=webpack:///./src/api/application/application.ts?"); /***/ }), /***/ "./src/api/base.ts": /*!*************************!*\ !*** ./src/api/base.ts ***! \*************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst promises_1 = __webpack_require__(/*! ../util/promises */ \"./src/util/promises.ts\");\r\nclass Base {\r\n constructor(wire) {\r\n this.isNodeEnvironment = () => {\r\n return this.wire.environment.constructor.name === 'NodeEnvironment';\r\n };\r\n this.isOpenFinEnvironment = () => {\r\n return this.wire.environment.constructor.name === 'OpenFinEnvironment';\r\n };\r\n this.wire = wire;\r\n }\r\n get topic() {\r\n return this._topic || this.constructor.name.replace('_', '').toLowerCase();\r\n }\r\n set topic(t) {\r\n this._topic = t;\r\n }\r\n get me() {\r\n return this.wire.me;\r\n }\r\n}\r\nexports.Base = Base;\r\nclass EmitterBase extends Base {\r\n constructor(wire, emitterAccessor) {\r\n super(wire);\r\n this.emitterAccessor = emitterAccessor;\r\n this.eventNames = () => this.hasEmitter() ? this.getEmitter().eventNames() : [];\r\n this.emit = (eventName, payload, ...args) => {\r\n return this.hasEmitter()\r\n ? this.getEmitter().emit(eventName, payload, ...args)\r\n : false;\r\n };\r\n this.hasEmitter = () => this.wire.eventAggregator.has(this.emitterAccessor);\r\n this.getEmitter = () => this.wire.eventAggregator.get(this.emitterAccessor);\r\n this.listeners = (type) => this.hasEmitter() ? this.getEmitter().listeners(type) : [];\r\n this.listenerCount = (type) => this.hasEmitter() ? this.getEmitter().listenerCount(type) : 0;\r\n this.registerEventListener = async (eventType, options = {}) => {\r\n const runtimeEvent = Object.assign({}, this.identity, {\r\n timestamp: options.timestamp || Date.now(),\r\n topic: this.topic,\r\n type: eventType\r\n });\r\n const emitter = this.getEmitter();\r\n const refCount = emitter.listenerCount(runtimeEvent.type);\r\n if (!refCount) {\r\n await this.wire.sendAction('subscribe-to-desktop-event', runtimeEvent);\r\n }\r\n return emitter;\r\n };\r\n this.deregisterEventListener = async (eventType, options = {}) => {\r\n if (this.hasEmitter()) {\r\n const runtimeEvent = Object.assign({}, this.identity, {\r\n timestamp: options.timestamp || Date.now(),\r\n topic: this.topic,\r\n type: eventType\r\n });\r\n const emitter = this.getEmitter();\r\n const refCount = emitter.listenerCount(runtimeEvent.type);\r\n const newRefCount = refCount - 1;\r\n if (newRefCount === 0) {\r\n await this.wire.sendAction('unsubscribe-to-desktop-event', runtimeEvent);\r\n if (emitter.eventNames && emitter.eventNames().length === 0) {\r\n this.wire.eventAggregator.delete(this.emitterAccessor);\r\n return;\r\n }\r\n }\r\n return emitter;\r\n }\r\n // This will only be reached if unsubscribe from event that does not exist but do not want to error here\r\n return Promise.resolve();\r\n };\r\n this.on = async (eventType, listener, options) => {\r\n const emitter = await this.registerEventListener(eventType, options);\r\n emitter.on(eventType, listener);\r\n return this;\r\n };\r\n this.addListener = this.on;\r\n this.once = async (eventType, listener, options) => {\r\n const deregister = () => this.deregisterEventListener(eventType);\r\n const emitter = await this.registerEventListener(eventType, options);\r\n emitter.once(eventType, deregister);\r\n emitter.once(eventType, listener);\r\n return this;\r\n };\r\n this.prependListener = async (eventType, listener, options) => {\r\n const emitter = await this.registerEventListener(eventType, options);\r\n emitter.prependListener(eventType, listener);\r\n return this;\r\n };\r\n this.prependOnceListener = async (eventType, listener, options) => {\r\n const deregister = () => this.deregisterEventListener(eventType);\r\n const emitter = await this.registerEventListener(eventType, options);\r\n emitter.prependOnceListener(eventType, listener);\r\n emitter.once(eventType, deregister);\r\n return this;\r\n };\r\n this.removeListener = async (eventType, listener, options) => {\r\n const emitter = await this.deregisterEventListener(eventType, options);\r\n if (emitter) {\r\n emitter.removeListener(eventType, listener);\r\n }\r\n return this;\r\n };\r\n this.deregisterAllListeners = async (eventType) => {\r\n const runtimeEvent = Object.assign({}, this.identity, {\r\n type: eventType,\r\n topic: this.topic\r\n });\r\n if (this.hasEmitter()) {\r\n await this.wire.sendAction('unsubscribe-to-desktop-event', runtimeEvent);\r\n const emitter = this.getEmitter();\r\n emitter.removeAllListeners(eventType);\r\n if (emitter.eventNames().length === 0) {\r\n this.wire.eventAggregator.delete(this.emitterAccessor);\r\n return;\r\n }\r\n return emitter;\r\n }\r\n };\r\n this.removeAllListeners = async (eventType) => {\r\n const removeByEvent = async (event) => {\r\n const emitter = await this.deregisterAllListeners(event);\r\n if (emitter) {\r\n emitter.removeAllListeners(event);\r\n }\r\n };\r\n if (eventType) {\r\n await removeByEvent(eventType);\r\n }\r\n else if (this.hasEmitter()) {\r\n const events = this.getEmitter().eventNames();\r\n await promises_1.promiseMap(events, removeByEvent);\r\n }\r\n return this;\r\n };\r\n this.listeners = (event) => this.hasEmitter()\r\n ? this.getEmitter().listeners(event)\r\n : [];\r\n }\r\n}\r\nexports.EmitterBase = EmitterBase;\r\nclass Reply {\r\n}\r\nexports.Reply = Reply;\r\n\n\n//# sourceURL=webpack:///./src/api/base.ts?"); /***/ }), /***/ "./src/api/clipboard/clipboard.ts": /*!****************************************!*\ !*** ./src/api/clipboard/clipboard.ts ***! \****************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst base_1 = __webpack_require__(/*! ../base */ \"./src/api/base.ts\");\r\n/**\r\n * WriteRequestType interface\r\n * @typedef { object } WriteRequestType\r\n * @property { string } name The name of the running application\r\n * @property { string } uuid The uuid of the running application\r\n */\r\n/**\r\n * The Clipboard API allows reading and writing to the clipboard in multiple formats.\r\n * @namespace\r\n*/\r\nclass Clipboard extends base_1.Base {\r\n /**\r\n * Writes data into the clipboard as plain text\r\n * @param { WriteRequestType } writeObj This object is described in the WriteRequestType typeof\r\n * @return {Promise.}\r\n * @tutorial Clipboard.writeText\r\n */\r\n writeText(writeObj) {\r\n return this.wire.sendAction('clipboard-write-text', writeObj).then(() => undefined);\r\n }\r\n /**\r\n * Read the content of the clipboard as plain text\r\n * @param { string } type Clipboard Type\r\n * @return {Promise.}\r\n * @tutorial Clipboard.readText\r\n */\r\n readText(type) {\r\n return this.wire.sendAction('clipboard-read-text', type)\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Writes data into the clipboard as Html\r\n * @param { WriteRequestType } writeObj This object is described in the WriteRequestType typedef\r\n * @return {Promise.}\r\n * @tutorial Clipboard.writeHtml\r\n */\r\n writeHtml(writeObj) {\r\n return this.wire.sendAction('clipboard-write-html', writeObj).then(() => undefined);\r\n }\r\n /**\r\n * Read the content of the clipboard as Html\r\n * @param { string } type Clipboard Type\r\n * @return {Promise.}\r\n * @tutorial Clipboard.readHtml\r\n */\r\n readHtml(type) {\r\n return this.wire.sendAction('clipboard-read-html', type)\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Writes data into the clipboard as Rtf\r\n * @param { WriteRequestType } writeObj This object is described in the WriteRequestType typedef\r\n * @return {Promise.}\r\n * @tutorial Clipboard.writeRtf\r\n */\r\n writeRtf(writeObj) {\r\n return this.wire.sendAction('clipboard-write-rtf', writeObj).then(() => undefined);\r\n }\r\n /**\r\n * Read the content of the clipboard as Rtf\r\n * @param { string } type Clipboard Type\r\n * @return {Promise.}\r\n * @tutorial Clipboard.readRtf\r\n */\r\n readRtf(type) {\r\n return this.wire.sendAction('clipboard-read-rtf', type)\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Writes data into the clipboard\r\n * @param { WriteRequestType } writeObj This object is described in the WriteRequestType typedef\r\n * @return {Promise.}\r\n * @tutorial Clipboard.write\r\n */\r\n write(writeObj) {\r\n return this.wire.sendAction('clipboard-write', writeObj).then(() => undefined);\r\n }\r\n /**\r\n * Reads available formats for the clipboard type\r\n * @param { string } type Clipboard Type\r\n * @return {Promise.Array.}\r\n * @tutorial Clipboard.getAvailableFormats\r\n */\r\n getAvailableFormats(type) {\r\n return this.wire.sendAction('clipboard-read-formats', type)\r\n .then(({ payload }) => payload.data);\r\n }\r\n}\r\nexports.default = Clipboard;\r\n\n\n//# sourceURL=webpack:///./src/api/clipboard/clipboard.ts?"); /***/ }), /***/ "./src/api/events/emitterMap.ts": /*!**************************************!*\ !*** ./src/api/events/emitterMap.ts ***! \**************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("/* WEBPACK VAR INJECTION */(function(Buffer) {\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst events_1 = __webpack_require__(/*! events */ \"./node_modules/events/events.js\");\r\nclass EmitterMap {\r\n constructor() {\r\n this.storage = new Map();\r\n }\r\n hashKeys(keys) {\r\n const hashed = keys.map(normalizeString);\r\n return hashed.join('/');\r\n }\r\n get(keys) {\r\n const hash = this.hashKeys(keys);\r\n if (!this.storage.has(hash)) {\r\n this.storage.set(hash, new events_1.EventEmitter());\r\n }\r\n return this.storage.get(hash);\r\n }\r\n has(keys) {\r\n return this.storage.has(this.hashKeys(keys));\r\n }\r\n delete(keys) {\r\n const hash = this.hashKeys(keys);\r\n return this.storage.delete(hash);\r\n }\r\n}\r\nexports.EmitterMap = EmitterMap;\r\nfunction normalizeString(s) {\r\n const b = new Buffer(s);\r\n return b.toString('base64');\r\n}\r\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../node_modules/buffer/index.js */ \"./node_modules/buffer/index.js\").Buffer))\n\n//# sourceURL=webpack:///./src/api/events/emitterMap.ts?"); /***/ }), /***/ "./src/api/events/eventAggregator.ts": /*!*******************************************!*\ !*** ./src/api/events/eventAggregator.ts ***! \*******************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst emitterMap_1 = __webpack_require__(/*! ./emitterMap */ \"./src/api/events/emitterMap.ts\");\r\nfunction isEventMessage(message) {\r\n return message.action === 'process-desktop-event';\r\n}\r\nfunction isNotificationMessage(message) {\r\n return message.action === 'process-notification-event';\r\n}\r\nconst buildLocalPayload = (rawPayload) => {\r\n const { payload: { message }, type } = rawPayload;\r\n const payload = {};\r\n switch (type) {\r\n case 'message':\r\n payload.message = message;\r\n break;\r\n case 'show':\r\n case 'error':\r\n case 'click':\r\n case 'close':\r\n default: break;\r\n }\r\n return payload;\r\n};\r\nfunction mapKeyFromEvent(event) {\r\n const { topic } = event;\r\n if (topic === 'frame') {\r\n const { uuid, name } = event;\r\n return [topic, uuid, name];\r\n }\r\n if (topic === 'window') {\r\n const { uuid, name } = event;\r\n return [topic, uuid, name];\r\n }\r\n if (topic === 'application') {\r\n const { uuid } = event;\r\n return [topic, uuid];\r\n }\r\n return [topic];\r\n}\r\nclass EventAggregator extends emitterMap_1.EmitterMap {\r\n constructor() {\r\n super(...arguments);\r\n this.dispatchEvent = (message) => {\r\n if (isEventMessage(message)) {\r\n const { payload } = message;\r\n const accessor = mapKeyFromEvent(payload);\r\n if (this.has(accessor)) {\r\n this.get(accessor).emit(payload.type, payload);\r\n return true;\r\n }\r\n }\r\n else if (isNotificationMessage(message)) {\r\n const { payload: { notificationId }, type } = message.payload;\r\n const accessor = ['notification', '' + notificationId];\r\n if (this.has(accessor)) {\r\n this.get(accessor).emit(type, buildLocalPayload(message.payload));\r\n return true;\r\n }\r\n }\r\n return false;\r\n };\r\n }\r\n}\r\nexports.EventAggregator = EventAggregator;\r\n\n\n//# sourceURL=webpack:///./src/api/events/eventAggregator.ts?"); /***/ }), /***/ "./src/api/external-application/external-application.ts": /*!**************************************************************!*\ !*** ./src/api/external-application/external-application.ts ***! \**************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst base_1 = __webpack_require__(/*! ../base */ \"./src/api/base.ts\");\r\n/**\r\n * @lends ExternalApplication\r\n */\r\nclass ExternalApplicationModule extends base_1.Base {\r\n /**\r\n * Asynchronously returns an External Application object that represents an existing external application.\r\n * @param {string} uuid The UUID of the external application to be wrapped\r\n * @return {Promise.}\r\n * @tutorial ExternalApplication.wrap\r\n * @static\r\n */\r\n wrap(uuid) {\r\n return Promise.resolve(new ExternalApplication(this.wire, { uuid }));\r\n }\r\n /**\r\n * Synchronously returns an External Application object that represents an existing external application.\r\n * @param {string} uuid The UUID of the external application to be wrapped\r\n * @return {ExternalApplication}\r\n * @tutorial ExternalApplication.wrapSync\r\n * @static\r\n */\r\n wrapSync(uuid) {\r\n return new ExternalApplication(this.wire, { uuid });\r\n }\r\n}\r\nexports.default = ExternalApplicationModule;\r\n/**\r\n * @classdesc An ExternalApplication object representing an application. Allows\r\n * the developer to create, execute, show and close an external application as\r\n * well as listen to application events.\r\n * @class\r\n * @hideconstructor\r\n */\r\nclass ExternalApplication extends base_1.EmitterBase {\r\n constructor(wire, identity) {\r\n super(wire, ['external-application', identity.uuid]);\r\n this.identity = identity;\r\n }\r\n /**\r\n * Adds a listener to the end of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - Called whenever an event of the specified type occurs.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function addListener\r\n * @memberof ExternalApplication\r\n * @instance\r\n * @tutorial ExternalApplication.EventEmitter\r\n */\r\n /**\r\n * Adds a listener to the end of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - Called whenever an event of the specified type occurs.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function on\r\n * @memberof ExternalApplication\r\n * @instance\r\n * @tutorial ExternalApplication.EventEmitter\r\n */\r\n /**\r\n * Adds a one time listener for the event. The listener is invoked only the first time the event is fired, after which it is removed.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function once\r\n * @memberof ExternalApplication\r\n * @instance\r\n * @tutorial ExternalApplication.EventEmitter\r\n */\r\n /**\r\n * Adds a listener to the beginning of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function prependListener\r\n * @memberof ExternalApplication\r\n * @instance\r\n * @tutorial ExternalApplication.EventEmitter\r\n */\r\n /**\r\n * Adds a one time listener for the event. The listener is invoked only the first time the event is fired, after which it is removed.\r\n * The listener is added to the beginning of the listeners array.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function prependOnceListener\r\n * @memberof ExternalApplication\r\n * @instance\r\n * @tutorial ExternalApplication.EventEmitter\r\n */\r\n /**\r\n * Remove a listener from the listener array for the specified event.\r\n * Caution: Calling this method changes the array indices in the listener array behind the listener.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function removeListener\r\n * @memberof ExternalApplication\r\n * @instance\r\n * @tutorial ExternalApplication.EventEmitter\r\n */\r\n /**\r\n * Removes all listeners, or those of the specified event.\r\n * @param { string | symbol } [eventType] - The type of the event.\r\n * @return {Promise.}\r\n * @function removeAllListeners\r\n * @memberof ExternalApplication\r\n * @instance\r\n * @tutorial ExternalApplication.EventEmitter\r\n */\r\n /**\r\n * Retrieves information about the external application.\r\n * @return {Promise.}\r\n * @tutorial ExternalApplication.getInfo\r\n */\r\n getInfo() {\r\n return this.wire.sendAction('get-external-application-info', this.identity).then(({ payload }) => payload.data);\r\n }\r\n}\r\nexports.ExternalApplication = ExternalApplication;\r\n\n\n//# sourceURL=webpack:///./src/api/external-application/external-application.ts?"); /***/ }), /***/ "./src/api/fin.ts": /*!************************!*\ !*** ./src/api/fin.ts ***! \************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst events_1 = __webpack_require__(/*! events */ \"./node_modules/events/events.js\");\r\nconst system_1 = __webpack_require__(/*! ./system/system */ \"./src/api/system/system.ts\");\r\nconst window_1 = __webpack_require__(/*! ./window/window */ \"./src/api/window/window.ts\");\r\nconst application_1 = __webpack_require__(/*! ./application/application */ \"./src/api/application/application.ts\");\r\nconst interappbus_1 = __webpack_require__(/*! ./interappbus/interappbus */ \"./src/api/interappbus/interappbus.ts\");\r\nconst notification_1 = __webpack_require__(/*! ./notification/notification */ \"./src/api/notification/notification.ts\");\r\nconst clipboard_1 = __webpack_require__(/*! ./clipboard/clipboard */ \"./src/api/clipboard/clipboard.ts\");\r\nconst external_application_1 = __webpack_require__(/*! ./external-application/external-application */ \"./src/api/external-application/external-application.ts\");\r\nconst frame_1 = __webpack_require__(/*! ./frame/frame */ \"./src/api/frame/frame.ts\");\r\nconst global_hotkey_1 = __webpack_require__(/*! ./global-hotkey */ \"./src/api/global-hotkey/index.ts\");\r\nclass Fin extends events_1.EventEmitter {\r\n get me() {\r\n return this.wire.me;\r\n }\r\n constructor(wire) {\r\n super();\r\n this.wire = wire;\r\n this.System = new system_1.default(wire);\r\n this.Window = new window_1.default(wire);\r\n this.Application = new application_1.default(wire);\r\n this.InterApplicationBus = new interappbus_1.default(wire);\r\n this.Notification = new notification_1.default(wire);\r\n this.Clipboard = new clipboard_1.default(wire);\r\n this.ExternalApplication = new external_application_1.default(wire);\r\n this.Frame = new frame_1.default(wire);\r\n this.GlobalHotkey = new global_hotkey_1.default(wire);\r\n //Handle disconnect events\r\n wire.on('disconnected', () => {\r\n this.emit('disconnected');\r\n });\r\n }\r\n}\r\nexports.default = Fin;\r\n\n\n//# sourceURL=webpack:///./src/api/fin.ts?"); /***/ }), /***/ "./src/api/frame/frame.ts": /*!********************************!*\ !*** ./src/api/frame/frame.ts ***! \********************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst base_1 = __webpack_require__(/*! ../base */ \"./src/api/base.ts\");\r\n/**\r\n * @lends Frame\r\n */\r\n// tslint:disable-next-line\r\nclass _FrameModule extends base_1.Base {\r\n /**\r\n * Asynchronously returns a reference to the specified frame. The frame does not have to exist\r\n * @param {Identity} identity - the identity of the frame you want to wrap\r\n * @return {Promise.<_Frame>}\r\n * @tutorial Frame.wrap\r\n * @static\r\n */\r\n wrap(identity) {\r\n return Promise.resolve(new _Frame(this.wire, identity));\r\n }\r\n /**\r\n * Synchronously returns a reference to the specified frame. The frame does not have to exist\r\n * @param {Identity} identity - the identity of the frame you want to wrap\r\n * @return {_Frame}\r\n * @tutorial Frame.wrapSync\r\n * @static\r\n */\r\n wrapSync(identity) {\r\n return new _Frame(this.wire, identity);\r\n }\r\n /**\r\n * Asynchronously returns a reference to the current frame\r\n * @return {Promise.<_Frame>}\r\n * @tutorial Frame.getCurrent\r\n * @static\r\n */\r\n getCurrent() {\r\n return Promise.resolve(new _Frame(this.wire, this.me));\r\n }\r\n /**\r\n * Synchronously returns a reference to the current frame\r\n * @return {_Frame}\r\n * @tutorial Frame.getCurrentSync\r\n * @static\r\n */\r\n getCurrentSync() {\r\n return new _Frame(this.wire, this.me);\r\n }\r\n}\r\nexports.default = _FrameModule;\r\n/**\r\n * @classdesc Represents a way to interact with `iframes`. Facilitates discovery of current context\r\n * (iframe or main window) as well as the ability to listen for frame-specific events.\r\n * @class\r\n * @alias Frame\r\n * @hideconstructor\r\n */\r\n// tslint:disable-next-line\r\nclass _Frame extends base_1.EmitterBase {\r\n constructor(wire, identity) {\r\n super(wire, ['frame', identity.uuid, identity.name]);\r\n this.identity = identity;\r\n }\r\n /**\r\n * Adds the listener function to the end of the listeners array for the specified event type.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - Called whenever an event of the specified type occurs.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function addListener\r\n * @memberof Frame\r\n * @instance\r\n * @tutorial Frame.EventEmitter\r\n */\r\n /**\r\n * Adds a listener to the end of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - Called whenever an event of the specified type occurs.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function on\r\n * @memberof Frame\r\n * @instance\r\n * @tutorial Frame.EventEmitter\r\n */\r\n /**\r\n * Adds a one time listener for the event. The listener is invoked only the first time the event is fired, after which it is removed.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function once\r\n * @memberof Frame\r\n * @instance\r\n * @tutorial Frame.EventEmitter\r\n */\r\n /**\r\n * Adds a listener to the beginning of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function prependListener\r\n * @memberof Frame\r\n * @instance\r\n * @tutorial Frame.EventEmitter\r\n */\r\n /**\r\n * Adds a one time listener for the event. The listener is invoked only the first time the event is fired, after which it is removed.\r\n * The listener is added to the beginning of the listeners array.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function prependOnceListener\r\n * @memberof Frame\r\n * @instance\r\n * @tutorial Frame.EventEmitter\r\n */\r\n /**\r\n * Remove a listener from the listener array for the specified event.\r\n * Caution: Calling this method changes the array indices in the listener array behind the listener.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function removeListener\r\n * @memberof Frame\r\n * @instance\r\n * @tutorial Frame.EventEmitter\r\n */\r\n /**\r\n * Removes all listeners, or those of the specified event.\r\n * @param { string | symbol } [eventType] - The type of the event.\r\n * @return {Promise.}\r\n * @function removeAllListeners\r\n * @memberof Frame\r\n * @instance\r\n * @tutorial Frame.EventEmitter\r\n */\r\n /**\r\n * Returns a frame info object for the represented frame\r\n * @return {Promise.}\r\n * @tutorial Frame.getInfo\r\n */\r\n getInfo() {\r\n return this.wire.sendAction('get-frame-info', this.identity).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Returns a frame info object representing the window that the referenced iframe is\r\n * currently embedded in\r\n * @return {Promise.}\r\n * @tutorial Frame.getParentWindow\r\n */\r\n getParentWindow() {\r\n return this.wire.sendAction('get-parent-window', this.identity).then(({ payload }) => payload.data);\r\n }\r\n}\r\nexports._Frame = _Frame;\r\n\n\n//# sourceURL=webpack:///./src/api/frame/frame.ts?"); /***/ }), /***/ "./src/api/global-hotkey/index.ts": /*!****************************************!*\ !*** ./src/api/global-hotkey/index.ts ***! \****************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst base_1 = __webpack_require__(/*! ../base */ \"./src/api/base.ts\");\r\n/**\r\n * The GlobalHotkey module can register/unregister a global hotkeys.\r\n * @namespace\r\n */\r\nclass GlobalHotkey extends base_1.EmitterBase {\r\n constructor(wire) {\r\n super(wire, ['global-hotkey']);\r\n this.topic = 'global-hotkey';\r\n }\r\n /**\r\n * Registers a global hotkey with the operating system.\r\n * @return {Promise.}\r\n * @tutorial GlobalHotkey.register\r\n */\r\n async register(hotkey, listener) {\r\n await this.on(hotkey, listener);\r\n await this.wire.sendAction(\"global-hotkey-register\" /* REGISTER */, { hotkey });\r\n return void 0;\r\n }\r\n /**\r\n * Unregisters a global hotkey with the operating system.\r\n * @return {Promise.}\r\n * @tutorial GlobalHotkey.unregister\r\n */\r\n async unregister(hotkey) {\r\n await this.removeAllListeners(hotkey);\r\n await this.wire.sendAction(\"global-hotkey-unregister\" /* UNREGISTER */, { hotkey });\r\n return void 0;\r\n }\r\n /**\r\n * Unregisters all global hotkeys for the current application.\r\n * @return {Promise.}\r\n * @tutorial GlobalHotkey.unregisterAll\r\n */\r\n async unregisterAll() {\r\n await Promise.all(this.eventNames()\r\n .filter((name) => !(name === \"registered\" /* REGISTERED */ || name === \"unregistered\" /* UNREGISTERED */))\r\n .map((name) => this.removeAllListeners(name)));\r\n await this.wire.sendAction(\"global-hotkey-unregister-all\" /* UNREGISTER_ALL */, {});\r\n return void 0;\r\n }\r\n /**\r\n * Checks if a given hotkey has been registered\r\n * @return {Promise.}\r\n * @tutorial GlobalHotkey.isRegistered\r\n */\r\n async isRegistered(hotkey) {\r\n const { payload: { data } } = await this.wire.sendAction(\"global-hotkey-is-registered\" /* IS_REGISTERED */, { hotkey });\r\n return data;\r\n }\r\n}\r\nexports.default = GlobalHotkey;\r\n\n\n//# sourceURL=webpack:///./src/api/global-hotkey/index.ts?"); /***/ }), /***/ "./src/api/interappbus/channel/channel.ts": /*!************************************************!*\ !*** ./src/api/interappbus/channel/channel.ts ***! \************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst idOrResult = (func) => (...args) => {\r\n const res = func(...args);\r\n return res === undefined ? args[1] : res;\r\n};\r\nclass ChannelBase {\r\n constructor(providerIdentity, send) {\r\n this.defaultSet = false;\r\n this.providerIdentity = providerIdentity;\r\n this.subscriptions = new Map();\r\n this.defaultAction = () => {\r\n throw new Error('No action registered');\r\n };\r\n this.sendRaw = send;\r\n this.send = async (to, action, payload) => {\r\n const raw = await send('send-channel-message', Object.assign({}, to, { providerIdentity: this.providerIdentity, action, payload }))\r\n .catch(reason => {\r\n throw new Error(reason.message);\r\n });\r\n return raw.payload.data.result;\r\n };\r\n }\r\n async processAction(action, payload, senderIdentity) {\r\n try {\r\n const mainAction = this.subscriptions.has(action)\r\n ? this.subscriptions.get(action)\r\n : (payload, id) => this.defaultAction(action, payload, id);\r\n const preActionProcessed = this.preAction ? await this.preAction(action, payload, senderIdentity) : payload;\r\n const actionProcessed = await mainAction(preActionProcessed, senderIdentity);\r\n return this.postAction\r\n ? await this.postAction(action, actionProcessed, senderIdentity)\r\n : actionProcessed;\r\n }\r\n catch (e) {\r\n if (this.errorMiddleware) {\r\n return this.errorMiddleware(action, e, senderIdentity);\r\n }\r\n throw e;\r\n }\r\n }\r\n beforeAction(func) {\r\n if (this.preAction) {\r\n throw new Error('Already registered beforeAction middleware');\r\n }\r\n this.preAction = idOrResult(func);\r\n }\r\n onError(func) {\r\n if (this.errorMiddleware) {\r\n throw new Error('Already registered error middleware');\r\n }\r\n this.errorMiddleware = func;\r\n }\r\n afterAction(func) {\r\n if (this.postAction) {\r\n throw new Error('Already registered afterAction middleware');\r\n }\r\n this.postAction = idOrResult(func);\r\n }\r\n remove(action) {\r\n this.subscriptions.delete(action);\r\n }\r\n setDefaultAction(func) {\r\n if (this.defaultSet) {\r\n throw new Error('default action can only be set once');\r\n }\r\n else {\r\n this.defaultAction = func;\r\n this.defaultSet = true;\r\n }\r\n }\r\n register(topic, listener) {\r\n if (this.subscriptions.has(topic)) {\r\n throw new Error(`Subscription already registered for action: ${topic}. Unsubscribe before adding new subscription`);\r\n }\r\n else {\r\n this.subscriptions.set(topic, listener);\r\n return true;\r\n }\r\n }\r\n}\r\nexports.ChannelBase = ChannelBase;\r\n\n\n//# sourceURL=webpack:///./src/api/interappbus/channel/channel.ts?"); /***/ }), /***/ "./src/api/interappbus/channel/client.ts": /*!***********************************************!*\ !*** ./src/api/interappbus/channel/client.ts ***! \***********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst channel_1 = __webpack_require__(/*! ./channel */ \"./src/api/interappbus/channel/channel.ts\");\r\nclass ChannelClient extends channel_1.ChannelBase {\r\n constructor(providerIdentity, send) {\r\n super(providerIdentity, send);\r\n this.disconnectListener = () => undefined;\r\n }\r\n async dispatch(action, payload) {\r\n return this.send(this.providerIdentity, action, payload);\r\n }\r\n onDisconnection(listener) {\r\n this.disconnectListener = listener;\r\n }\r\n async disconnect() {\r\n const { channelName } = this.providerIdentity;\r\n await this.sendRaw('disconnect-from-channel', { channelName });\r\n const { channelId } = this.providerIdentity;\r\n this.removeChannel(channelId);\r\n }\r\n}\r\nexports.ChannelClient = ChannelClient;\r\n\n\n//# sourceURL=webpack:///./src/api/interappbus/channel/client.ts?"); /***/ }), /***/ "./src/api/interappbus/channel/index.ts": /*!**********************************************!*\ !*** ./src/api/interappbus/channel/index.ts ***! \**********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst client_1 = __webpack_require__(/*! ./client */ \"./src/api/interappbus/channel/client.ts\");\r\nconst provider_1 = __webpack_require__(/*! ./provider */ \"./src/api/interappbus/channel/provider.ts\");\r\nconst base_1 = __webpack_require__(/*! ../../base */ \"./src/api/base.ts\");\r\nclass Channel extends base_1.EmitterBase {\r\n constructor(wire) {\r\n super(wire, ['channel']);\r\n this.onmessage = (msg) => {\r\n if (msg.action === 'process-channel-message') {\r\n this.processChannelMessage(msg);\r\n return true;\r\n }\r\n else if (msg.action === 'process-channel-connection') {\r\n this.processChannelConnection(msg);\r\n return true;\r\n }\r\n return false;\r\n };\r\n this.topic = 'channel';\r\n this.channelMap = new Map();\r\n wire.registerMessageHandler(this.onmessage.bind(this));\r\n }\r\n async getAllChannels() {\r\n return this.wire.sendAction('get-all-channels')\r\n .then(({ payload }) => payload.data);\r\n }\r\n async onChannelConnect(listener) {\r\n await this.on('connected', listener);\r\n }\r\n async onChannelDisconnect(listener) {\r\n await this.on('disconnected', listener);\r\n }\r\n async connect(channelName, options) {\r\n if (!channelName || typeof channelName !== 'string') {\r\n throw new Error('Please provide a channelName string to connect to a channel.');\r\n }\r\n const opts = options || {};\r\n let resolver;\r\n let listener;\r\n const waitResponse = new Promise(resolve => {\r\n resolver = resolve;\r\n listener = (payload) => {\r\n if (channelName === payload.channelName) {\r\n this.removeListener('connected', listener);\r\n this.connect(channelName, opts).then(response => {\r\n resolve(response);\r\n });\r\n }\r\n };\r\n this.on('connected', listener);\r\n });\r\n try {\r\n const { payload: { data: providerIdentity } } = await this.wire.sendAction('connect-to-channel', Object.assign({ channelName }, opts));\r\n // If there isn't a matching channel, the above sendAction call will error out and go to catch, skipping the logic below\r\n if (resolver) {\r\n resolver();\r\n }\r\n this.removeListener('connected', listener);\r\n const channel = new client_1.ChannelClient(providerIdentity, this.wire.sendAction.bind(this.wire));\r\n const key = providerIdentity.channelId;\r\n this.channelMap.set(key, channel);\r\n //@ts-ignore use of protected property\r\n channel.removeChannel = this.removeChannelFromMap.bind(this);\r\n this.on('disconnected', (eventPayload) => {\r\n if (eventPayload.channelName === channelName) {\r\n this.removeChannelFromMap(key);\r\n //@ts-ignore use of private property\r\n channel.disconnectListener(eventPayload);\r\n }\r\n });\r\n return channel;\r\n }\r\n catch (e) {\r\n const shouldWait = Object.assign({ wait: true }, opts).wait;\r\n const internalNackMessage = 'internal-nack';\r\n if (shouldWait && e.message && e.message.includes(internalNackMessage)) {\r\n console.warn(`Channel not found for channelName: ${channelName}, waiting for channel creation.`);\r\n return await waitResponse;\r\n }\r\n else if (e.message === internalNackMessage) {\r\n throw new Error(`No channel found for channelName: ${channelName}`);\r\n }\r\n else {\r\n throw new Error(e);\r\n }\r\n }\r\n }\r\n async create(channelName) {\r\n if (!channelName) {\r\n throw new Error('Please provide a channelName to create a channel');\r\n }\r\n const { payload: { data: providerIdentity } } = await this.wire.sendAction('create-channel', { channelName });\r\n const channel = new provider_1.ChannelProvider(providerIdentity, this.wire.sendAction.bind(this.wire));\r\n const key = providerIdentity.channelId;\r\n this.channelMap.set(key, channel);\r\n //@ts-ignore use of protected property\r\n channel.removeChannel = this.removeChannelFromMap.bind(this);\r\n this.on('client-disconnected', (eventPayload) => {\r\n if (eventPayload.channelName === channelName) {\r\n channel.connections = channel.connections.filter(identity => {\r\n return identity.uuid !== eventPayload.uuid || identity.name !== eventPayload.name;\r\n });\r\n //@ts-ignore use of private property\r\n channel.disconnectListener(eventPayload);\r\n }\r\n });\r\n return channel;\r\n }\r\n removeChannelFromMap(mapKey) {\r\n this.channelMap.delete(mapKey);\r\n }\r\n async processChannelMessage(msg) {\r\n const { senderIdentity, providerIdentity, action, ackToSender, payload } = msg.payload;\r\n const key = providerIdentity.channelId;\r\n const bus = this.channelMap.get(key);\r\n if (!bus) {\r\n ackToSender.payload.success = false;\r\n ackToSender.payload.reason = `Client connection with identity ${JSON.stringify(this.wire.me)} no longer connected.`;\r\n return this.wire.sendRaw(ackToSender);\r\n }\r\n try {\r\n const res = await bus.processAction(action, payload, senderIdentity);\r\n ackToSender.payload.payload = ackToSender.payload.payload || {};\r\n ackToSender.payload.payload.result = res;\r\n this.wire.sendRaw(ackToSender);\r\n }\r\n catch (e) {\r\n ackToSender.payload.success = false;\r\n ackToSender.payload.reason = e.message;\r\n this.wire.sendRaw(ackToSender);\r\n }\r\n }\r\n async processChannelConnection(msg) {\r\n const { clientIdentity, providerIdentity, ackToSender, payload } = msg.payload;\r\n const key = providerIdentity.channelId;\r\n const bus = this.channelMap.get(key);\r\n if (!bus) {\r\n ackToSender.payload.success = false;\r\n ackToSender.payload.reason = `Channel \"${providerIdentity.channelName}\" has been destroyed.`;\r\n return this.wire.sendRaw(ackToSender);\r\n }\r\n try {\r\n if (!(bus instanceof provider_1.ChannelProvider)) {\r\n throw Error('Cannot connect to a channel client');\r\n }\r\n const res = await bus.processConnection(clientIdentity, payload);\r\n ackToSender.payload.payload = ackToSender.payload.payload || {};\r\n ackToSender.payload.payload.result = res;\r\n this.wire.sendRaw(ackToSender);\r\n }\r\n catch (e) {\r\n ackToSender.payload.success = false;\r\n ackToSender.payload.reason = e.message;\r\n this.wire.sendRaw(ackToSender);\r\n }\r\n }\r\n}\r\nexports.Channel = Channel;\r\n\n\n//# sourceURL=webpack:///./src/api/interappbus/channel/index.ts?"); /***/ }), /***/ "./src/api/interappbus/channel/provider.ts": /*!*************************************************!*\ !*** ./src/api/interappbus/channel/provider.ts ***! \*************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst channel_1 = __webpack_require__(/*! ./channel */ \"./src/api/interappbus/channel/channel.ts\");\r\nclass ChannelProvider extends channel_1.ChannelBase {\r\n constructor(providerIdentity, send) {\r\n super(providerIdentity, send);\r\n this.connectListener = () => undefined;\r\n this.disconnectListener = () => undefined;\r\n this.connections = [];\r\n }\r\n dispatch(to, action, payload) {\r\n return this.send(to, action, payload);\r\n }\r\n async processConnection(senderId, payload) {\r\n this.connections.push(senderId);\r\n return this.connectListener(senderId, payload);\r\n }\r\n publish(action, payload) {\r\n return this.connections.map(to => this.send(to, action, payload));\r\n }\r\n onConnection(listener) {\r\n this.connectListener = listener;\r\n }\r\n onDisconnection(listener) {\r\n this.disconnectListener = listener;\r\n }\r\n async destroy() {\r\n const { channelName } = this.providerIdentity;\r\n await this.sendRaw('destroy-channel', { channelName });\r\n const { channelId } = this.providerIdentity;\r\n this.removeChannel(channelId);\r\n }\r\n}\r\nexports.ChannelProvider = ChannelProvider;\r\n\n\n//# sourceURL=webpack:///./src/api/interappbus/channel/provider.ts?"); /***/ }), /***/ "./src/api/interappbus/interappbus.ts": /*!********************************************!*\ !*** ./src/api/interappbus/interappbus.ts ***! \********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("/* WEBPACK VAR INJECTION */(function(Buffer) {\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst base_1 = __webpack_require__(/*! ../base */ \"./src/api/base.ts\");\r\nconst ref_counter_1 = __webpack_require__(/*! ../../util/ref-counter */ \"./src/util/ref-counter.ts\");\r\nconst events_1 = __webpack_require__(/*! events */ \"./node_modules/events/events.js\");\r\nconst index_1 = __webpack_require__(/*! ./channel/index */ \"./src/api/interappbus/channel/index.ts\");\r\n/**\r\n * A messaging bus that allows for pub/sub messaging between different applications.\r\n * @namespace\r\n*/\r\nclass InterApplicationBus extends base_1.Base {\r\n constructor(wire) {\r\n super(wire);\r\n this.events = {\r\n subscriberAdded: 'subscriber-added',\r\n subscriberRemoved: 'subscriber-removed'\r\n };\r\n this.refCounter = new ref_counter_1.default();\r\n //tslint:disable-next-line\r\n this.Channel = new index_1.Channel(wire);\r\n this.emitter = new events_1.EventEmitter();\r\n wire.registerMessageHandler(this.onmessage.bind(this));\r\n this.on = this.emitter.on.bind(this.emitter);\r\n this.removeAllListeners = this.emitter.removeAllListeners.bind(this.emitter);\r\n }\r\n /**\r\n * Publishes a message to all applications running on OpenFin Runtime that\r\n * are subscribed to the specified topic.\r\n * @param { string } topic The topic on which the message is sent\r\n * @param { any } message The message to be published. Can be either a primitive\r\n * data type (string, number, or boolean) or composite data type (object, array)\r\n * that is composed of other primitive or composite data types\r\n * @return {Promise.}\r\n * @tutorial InterApplicationBus.publish\r\n */\r\n publish(topic, message) {\r\n return this.wire.sendAction('publish-message', {\r\n topic,\r\n message,\r\n sourceWindowName: this.me.name\r\n }).then(() => undefined);\r\n }\r\n /**\r\n * Sends a message to a specific application on a specific topic.\r\n * @param { Identity } destination The uuid of the application to which the message is sent\r\n * @param { string } topic The topic on which the message is sent\r\n * @param { any } message The message to be sent. Can be either a primitive data\r\n * type (string, number, or boolean) or composite data type (object, array) that\r\n * is composed of other primitive or composite data types\r\n * @return {Promise.}\r\n * @tutorial InterApplicationBus.send\r\n */\r\n send(destination, topic, message) {\r\n return this.wire.sendAction('send-message', {\r\n destinationUuid: destination.uuid,\r\n destinationWindowName: destination.name,\r\n topic,\r\n message,\r\n sourceWindowName: this.me.name\r\n }).then(() => undefined);\r\n }\r\n /**\r\n * Subscribes to messages from the specified application on the specified topic.\r\n * If the subscription is for a uuid, [name], topic combination that has already\r\n * been published to upon subscription you will receive the last 20 missed messages\r\n * in the order they were published.\r\n * @param { Identity } source This object is described in the Identity in the typedef\r\n * @param { string } topic The topic on which the message is sent\r\n * @param { function } listener A function that is called when a message has\r\n * been received. It is passed the message, uuid and name of the sending application.\r\n * The message can be either a primitive data type (string, number, or boolean) or\r\n * composite data type (object, array) that is composed of other primitive or composite\r\n * data types\r\n * @return {Promise.}\r\n * @tutorial InterApplicationBus.subcribe\r\n */\r\n subscribe(source, topic, listener) {\r\n const subKey = this.createSubscriptionKey(source.uuid, source.name || '*', topic);\r\n const sendSubscription = () => {\r\n return this.wire.sendAction('subscribe', {\r\n sourceUuid: source.uuid,\r\n sourceWindowName: source.name || '*',\r\n topic,\r\n destinationWindowName: this.me.name\r\n });\r\n };\r\n const alreadySubscribed = () => {\r\n // tslint:disable-next-line\r\n return new Promise(r => r).then(() => undefined);\r\n };\r\n this.emitter.on(subKey, listener);\r\n return this.refCounter.actOnFirst(subKey, sendSubscription, alreadySubscribed);\r\n }\r\n /**\r\n * Unsubscribes to messages from the specified application on the specified topic.\r\n * @param { Identity } source This object is described in the Identity in the typedef\r\n * @param { string } topic The topic on which the message is sent\r\n * @param { function } listener A callback previously registered with subscribe()\r\n * @return {Promise.}\r\n * @tutorial InterApplicationBus.unsubscribe\r\n */\r\n unsubscribe(source, topic, listener) {\r\n const subKey = this.createSubscriptionKey(source.uuid, source.name || '*', topic);\r\n const sendUnsubscription = () => {\r\n return this.wire.sendAction('unsubscribe', {\r\n sourceUuid: source.uuid,\r\n sourceWindowName: source.name || '*',\r\n topic,\r\n destinationWindowName: this.me.name\r\n });\r\n };\r\n const dontSendUnsubscription = () => {\r\n // tslint:disable-next-line\r\n return new Promise(r => r).then(() => undefined);\r\n };\r\n this.emitter.removeListener(subKey, listener);\r\n return this.refCounter.actOnLast(subKey, sendUnsubscription, dontSendUnsubscription);\r\n }\r\n processMessage(message) {\r\n const { payload: { message: payloadMessage, sourceWindowName, sourceUuid, topic } } = message;\r\n const keys = [\r\n this.createSubscriptionKey(sourceUuid, sourceWindowName, topic),\r\n this.createSubscriptionKey(sourceUuid, '*', topic),\r\n this.createSubscriptionKey('*', '*', topic)\r\n ];\r\n const idOfSender = { uuid: sourceUuid, name: sourceWindowName };\r\n keys.forEach((key) => {\r\n this.emitter.emit(key, payloadMessage, idOfSender);\r\n });\r\n }\r\n emitSubscriverEvent(type, message) {\r\n const { payload: { name, uuid, topic } } = message;\r\n const payload = { name, uuid, topic };\r\n this.emitter.emit(type, payload);\r\n }\r\n createSubscriptionKey(uuid, name, topic) {\r\n const n = name || '*';\r\n if (!(uuid && n && topic)) {\r\n throw new Error('Missing uuid, name, or topic string');\r\n }\r\n return createKey(uuid, n, topic);\r\n }\r\n onmessage(message) {\r\n const { action } = message;\r\n switch (action) {\r\n case 'process-message':\r\n this.processMessage(message);\r\n break;\r\n case this.events.subscriberAdded:\r\n this.emitSubscriverEvent(this.events.subscriberAdded, message);\r\n break;\r\n case this.events.subscriberRemoved:\r\n this.emitSubscriverEvent(this.events.subscriberRemoved, message);\r\n break;\r\n default: break;\r\n }\r\n return true;\r\n }\r\n}\r\nexports.default = InterApplicationBus;\r\nclass InterAppPayload {\r\n}\r\nexports.InterAppPayload = InterAppPayload;\r\nfunction createKey(...toHash) {\r\n return toHash.map((item) => {\r\n return (new Buffer('' + item)).toString('base64');\r\n }).join('/');\r\n}\r\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../node_modules/buffer/index.js */ \"./node_modules/buffer/index.js\").Buffer))\n\n//# sourceURL=webpack:///./src/api/interappbus/interappbus.ts?"); /***/ }), /***/ "./src/api/notification/notification.ts": /*!**********************************************!*\ !*** ./src/api/notification/notification.ts ***! \**********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst base_1 = __webpack_require__(/*! ../base */ \"./src/api/base.ts\");\r\nconst events = {\r\n show: 'show',\r\n close: 'close',\r\n error: 'error',\r\n click: 'click',\r\n message: 'message'\r\n};\r\nclass NotificationOptions {\r\n // tslint:disable-next-line\r\n constructor(options = {}, identity, notificationId) {\r\n const { url, message, timeout, ignoreMouseOver } = options;\r\n this.url = url;\r\n this.message = message || null;\r\n this.timeout = timeout;\r\n this.notificationId = notificationId;\r\n this.uuidOfProxiedApp = identity.uuid;\r\n this.ignoreMouseOver = ignoreMouseOver;\r\n }\r\n}\r\nexports.NotificationOptions = NotificationOptions;\r\n/**\r\n * @classdesc A Notification object represents a window on OpenFin Runtime which\r\n * is shown briefly to the user on the bottom-right corner of the primary monitor.\r\n * A notification is typically used to alert the user of some important event which\r\n * requires his or her attention. Notifications are a child or your application that\r\n * are controlled by the runtime.\r\n * @class\r\n * @alias Notification\r\n * @hideconstructor\r\n */\r\n// tslint:disable-next-line\r\nclass _Notification extends base_1.EmitterBase {\r\n constructor(wire, options) {\r\n super(wire, ['notification', '' + options.notificationId]);\r\n this.listenerList = ['newListener'];\r\n this.unhookAllListeners = () => {\r\n this.listenerList.forEach(event => {\r\n this.removeAllListeners(event);\r\n });\r\n this.listenerList.length = 0;\r\n };\r\n this.options = options;\r\n this.url = options.url;\r\n this.timeout = options.timeout;\r\n this.message = options.message;\r\n this.notificationId = options.notificationId;\r\n this.on('newListener', (event) => {\r\n this.listenerList.push(event);\r\n });\r\n // give any user added listeners a chance to run then unhook\r\n this.on('close', () => {\r\n setTimeout(this.unhookAllListeners, 1);\r\n });\r\n }\r\n /**\r\n * Invoked when the notification is shown\r\n * @return {Promise.}\r\n * @tutorial Notification.show\r\n */\r\n async show() {\r\n if (!this.url) {\r\n throw new Error('Notifications require a url');\r\n }\r\n await this.wire.sendAction('send-action-to-notifications-center', {\r\n action: 'create-notification',\r\n payload: {\r\n url: this.url,\r\n notificationId: this.options.notificationId,\r\n message: {\r\n message: this.message\r\n },\r\n timeout: this.timeout\r\n }\r\n });\r\n }\r\n /**\r\n * Sends a message to the notification.\r\n * @param { any } message The message to be sent to the notification.\r\n * Can be either a primitive data type (string, number, or boolean)\r\n * or composite data type (object, array) that is composed of other\r\n * primitive or composite data types\r\n * @return {Promise.}\r\n * @tutorial Notification.sendMessage\r\n */\r\n async sendMessage(message) {\r\n await this.wire.sendAction('send-action-to-notifications-center', {\r\n action: 'send-notification-message',\r\n payload: {\r\n notificationId: this.options.notificationId,\r\n message: {\r\n message\r\n }\r\n }\r\n });\r\n }\r\n /**\r\n * Closes the notification\r\n * @return {Promise.}\r\n * @tutorial Notification.close\r\n */\r\n async close() {\r\n await this.wire.sendAction('send-action-to-notifications-center', {\r\n action: 'close-notification',\r\n payload: {\r\n notificationId: this.options.notificationId\r\n }\r\n });\r\n }\r\n}\r\nexports._Notification = _Notification;\r\n/**\r\n * @lends Notification\r\n */\r\n// tslint:disable-next-line\r\nclass _NotificationModule extends base_1.Base {\r\n constructor() {\r\n super(...arguments);\r\n this.nextNoteId = 0;\r\n this.events = events;\r\n }\r\n genNoteId() {\r\n // tslint:disable-next-line\r\n return ++this.nextNoteId;\r\n }\r\n ;\r\n /**\r\n * Creates a new Notification.\r\n * @param { object } options\r\n * @return {_Notification}\r\n * @tutorial Notification.create\r\n * @static\r\n */\r\n create(options) {\r\n const noteOptions = new NotificationOptions(options, this.me, this.genNoteId());\r\n return new _Notification(this.wire, noteOptions);\r\n }\r\n ;\r\n}\r\nexports.default = _NotificationModule;\r\n\n\n//# sourceURL=webpack:///./src/api/notification/notification.ts?"); /***/ }), /***/ "./src/api/system/system.ts": /*!**********************************!*\ !*** ./src/api/system/system.ts ***! \**********************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst base_1 = __webpack_require__(/*! ../base */ \"./src/api/base.ts\");\r\nconst transport_errors_1 = __webpack_require__(/*! ../../transport/transport-errors */ \"./src/transport/transport-errors.ts\");\r\nconst window_1 = __webpack_require__(/*! ../window/window */ \"./src/api/window/window.ts\");\r\n/**\r\n * AppAssetInfo interface\r\n * @typedef { object } AppAssetInfo\r\n * @property { string } src The URL to a zip file containing the package files (executables, dlls, etc…)\r\n * @property { string } alias The name of the asset\r\n * @property { string } version The version of the package\r\n * @property { string } target Specify default executable to launch. This option can be overridden in launchExternalProcess\r\n * @property { string } args The default command line arguments for the aforementioned target.\r\n * @property { boolean } mandatory When set to true, the app will fail to load if the asset cannot be downloaded.\r\n * When set to false, the app will continue to load if the asset cannot be downloaded. (Default: true)\r\n */\r\n/**\r\n * AppAssetRequest interface\r\n * @typedef { object } AppAssetRequest\r\n * @property { string } alias The name of the asset\r\n */\r\n/**\r\n * ApplicationInfo interface\r\n * @typedef { object } ApplicationInfo\r\n * @property { boolean } isRunning true when the application is running\r\n * @property { string } uuid uuid of the application\r\n * @property { string } parentUuid uuid of the application that launches this application\r\n */\r\n/**\r\n * @typedef { object } ClearCacheOption\r\n * @summary Clear cache options.\r\n * @desc These are the options required by the clearCache function.\r\n *\r\n * @property {boolean} appcache html5 application cache\r\n * @property {boolean} cache browser data cache for html files and images\r\n * @property {boolean} cookies browser cookies\r\n * @property {boolean} localStorage browser data that can be used across sessions\r\n */\r\n/**\r\n * CookieInfo interface\r\n * @typedef { object } CookieInfo\r\n * @property { string } name The name of the cookie\r\n * @property { string } domain The domain of the cookie\r\n * @property { string } path The path of the cookie\r\n */\r\n/**\r\n * CookieOption interface\r\n * @typedef { object } CookieOption\r\n * @property { string } name The name of the cookie\r\n */\r\n/**\r\n * CpuInfo interface\r\n * @typedef { object } CpuInfo\r\n * @property { string } model The model of the cpu\r\n * @property { number } speed The number in MHz\r\n * @property { Time } times The numbers of milliseconds the CPU has spent in different modes.\r\n */\r\n/**\r\n* CrashReporterOption interface\r\n* @typedef { object } CrashReporterOption\r\n* @property { boolean } diagnosticMode In diagnostic mode the crash reporter will send diagnostic logs to\r\n* the OpenFin reporting service on runtime shutdown\r\n* @property { boolean } isRunning check if it's running\r\n*/\r\n/**\r\n * DipRect interface\r\n * @typedef { object } DipRect\r\n * @property { Rect } dipRect The DIP coordinates\r\n * @property { Rect } scaledRect The scale coordinates\r\n */\r\n/**\r\n * DipScaleRects interface\r\n * @typedef { object } DipScaleRects\r\n * @property { Rect } dipRect The DIP coordinates\r\n * @property { Rect } scaledRect The scale coordinates\r\n */\r\n/**\r\n * DownloadPreloadInfo interface\r\n * @typedef { object } DownloadPreloadInfo\r\n * @desc downloadPreloadScripts function return value\r\n * @property { string } url url to the preload script\r\n * @property { string } error error during preload script acquisition\r\n * @property { boolean } succeess download operation success\r\n */\r\n/**\r\n * DownloadPreloadOption interface\r\n * @typedef { object } DownloadPreloadOption\r\n * @desc These are the options object required by the downloadPreloadScripts function\r\n * @property { string } url url to the preload script\r\n */\r\n/**\r\n * Entity interface\r\n * @typedef { object } Entity\r\n * @property { string } type The type of the entity\r\n * @property { string } uuid The uuid of the entity\r\n */\r\n/**\r\n * EntityInfo interface\r\n * @typedef { object } EntityInfo\r\n * @property { string } name The name of the entity\r\n * @property { string } uuid The uuid of the entity\r\n * @property { Identity } parent The parent of the entity\r\n * @property { string } entityType The type of the entity\r\n */\r\n/**\r\n * ExternalApplicationInfo interface\r\n * @typedef { object } ExternalApplicationInfo\r\n * @property { Identity } parent The parent identity\r\n */\r\n/**\r\n * ExternalConnection interface\r\n * @typedef { object } ExternalConnection\r\n * @property { string } token The token to broker an external connection\r\n * @property { string } uuid The uuid of the external connection\r\n */\r\n/**\r\n * ExternalProcessRequestType interface\r\n * @typedef { object } ExternalProcessRequestType\r\n * @property { string } path The file path to where the running application resides\r\n * @property { string } arguments The argument passed to the running application\r\n * @property { LaunchExternalProcessListener } listener This is described in the {LaunchExternalProcessListner} type definition\r\n */\r\n/**\r\n * FrameInfo interface\r\n * @typedef { object } FrameInfo\r\n * @property { string } name The name of the frame\r\n * @property { string } uuid The uuid of the frame\r\n * @property { entityType } entityType The entity type, could be 'window', 'iframe', 'external connection' or 'unknown'\r\n * @property { Identity } parent The parent identity\r\n */\r\n/**\r\n * GetLogRequestType interface\r\n * @typedef { object } GetLogRequestType\r\n * @property { string } name The name of the running application\r\n * @property { number } endFile The file length of the log file\r\n * @property { number } sizeLimit The set size limit of the log file\r\n */\r\n/**\r\n * GpuInfo interface\r\n * @typedef { object } GpuInfo\r\n * @property { string } name The graphics card name\r\n */\r\n/**\r\n * HostSpecs interface\r\n * @typedef { object } HostSpecs\r\n * @property { boolean } aeroGlassEnabled Value to check if Aero Glass theme is supported on Windows platforms\r\n * @property { string } arch \"x86\" for 32-bit or \"x86_64\" for 64-bit\r\n * @property { Array } cpus The same payload as Node's os.cpus()\r\n * @property { GpuInfo } gpu The graphics card name\r\n * @property { number } memory The same payload as Node's os.totalmem()\r\n * @property { string } name The OS name and version/edition\r\n * @property { boolean } screenSaver Value to check if screensaver is running. Supported on Windows only\r\n */\r\n/**\r\n * Identity interface\r\n * @typedef { object } Identity\r\n * @property { string } name The name of the application\r\n * @property { string } uuid The uuid of the application\r\n */\r\n/**\r\n * LogInfo interface\r\n * @typedef { object } LogInfo\r\n * @property { string } name The filename of the log\r\n * @property { number } size The size of the log in bytes\r\n * @property { string } date The unix time at which the log was created \"Thu Jan 08 2015 14:40:30 GMT-0500 (Eastern Standard Time)\"\"\r\n */\r\n/**\r\n * MonitorDetails interface\r\n * @typedef { object } MonitorDetails\r\n * @property { DipScaleRects } available The available DIP scale coordinates\r\n * @property { Rect } availableRect The available monitor coordinates\r\n * @property { string } deviceId The device id of the display\r\n * @property { boolean } displayDeviceActive true if the display is active\r\n * @property { number } deviceScaleFactor The device scale factor\r\n * @property { Rect } monitorRect The monitor coordinates\r\n * @property { string } name The name of the display\r\n * @property { Point } dpi The dots per inch\r\n * @property { DipScaleRects } monitor The monitor coordinates\r\n */\r\n/**\r\n * MonitorInfo interface\r\n * @typedef { object } MonitorInfo\r\n * @property { number } deviceScaleFactor The device scale factor\r\n * @property { Point } dpi The dots per inch\r\n * @property { Array } nonPrimaryMonitors The array of monitor details\r\n * @property { MonitorDetails } primaryMonitor The monitor details\r\n * @property { string } reason always \"api-query\"\r\n * @property { TaskBar } taskBar The taskbar on monitor\r\n * @property { DipRect } virtualScreen The virtual display screen coordinates\r\n */\r\n/**\r\n * @typedef { verbose | info | warning | error | fatal } LogLevel\r\n * @summary Log verbosity levels.\r\n * @desc Describes the minimum level (inclusive) above which logs will be written\r\n *\r\n * @property { string } verbose all logs written\r\n * @property { string } info info and above\r\n * @property { string } warning warning and above\r\n * @property { string } error error and above\r\n * @property { string } fatal fatal only, indicates a crash is imminent\r\n */\r\n/**\r\n * PointTopLeft interface\r\n * @typedef { object } PointTopLeft\r\n * @property { number } top The mouse top position in virtual screen coordinates\r\n * @property { number } left The mouse left position in virtual screen coordinates\r\n */\r\n/**\r\n * Point interface\r\n * @typedef { object } Point\r\n * @property { number } x The mouse x position\r\n * @property { number } y The mouse y position\r\n */\r\n/**\r\n * ProcessInfo interface\r\n * @typedef { object } ProcessInfo\r\n * @property { numder } cpuUsage The percentage of total CPU usage\r\n * @property { string } name The application name\r\n * @property { number } nonPagedPoolUsage The current nonpaged pool usage in bytes\r\n * @property { number } pageFaultCount The number of page faults\r\n * @property { number } pagedPoolUsage The current paged pool usage in bytes\r\n * @property { number } pagefileUsage The total amount of memory in bytes that the memory manager has committed\r\n * @property { number } peakNonPagedPoolUsage The peak nonpaged pool usage in bytes\r\n * @property { number } peakPagedPoolUsage The peak paged pool usage in bytes\r\n * @property { number } peakPagefileUsage The peak value in bytes of pagefileUsage during the lifetime of this process\r\n * @property { number } peakWorkingSetSize The peak working set size in bytes\r\n * @property { number } processId The native process identifier\r\n * @property { string } uuid The application UUID\r\n * @property { nubmer } workingSetSize The current working set size (both shared and private data) in bytes\r\n */\r\n/**\r\n * ProxyConfig interface\r\n * @typedef { object } ProxyConfig\r\n * @property { string } proxyAddress The configured proxy address\r\n * @property { numder } proxyPort The configured proxy port\r\n * @property { string } type The proxy Type\r\n */\r\n/**\r\n * ProxyInfo interface\r\n * @typedef { object } ProxyInfo\r\n * @property { ProxyConfig } config The proxy config\r\n * @property { ProxySystemInfo } system The proxy system info\r\n */\r\n/**\r\n * ProxySystemInfo interface\r\n * @typedef { object } ProxySystemInfo\r\n * @property { string } autoConfigUrl The auto configuration url\r\n * @property { string } bypass The proxy bypass info\r\n * @property { boolean } enabled Value to check if a proxy is enabled\r\n * @property { string } proxy The proxy info\r\n */\r\n/**\r\n * Rect interface\r\n * @typedef { object } Rect\r\n * @property { number } bottom The bottom-most coordinate\r\n * @property { nubmer } left The left-most coordinate\r\n * @property { number } right The right-most coordinate\r\n * @property { nubmer } top The top-most coordinate\r\n */\r\n/**\r\n * RegistryInfo interface\r\n * @typedef { object } RegistryInfo\r\n * @property { any } data The registry data\r\n * @property { string } rootKey The registry root key\r\n * @property { string } subkey The registry key\r\n * @property { string } type The registry type\r\n * @property { string } value The registry value name\r\n */\r\n/**\r\n * RuntimeDownloadOptions interface\r\n * @typedef { object } RuntimeDownloadOptions\r\n * @desc These are the options object required by the downloadRuntime function.\r\n * @property { string } version The given version to download\r\n */\r\n/**\r\n * RuntimeInfo interface\r\n * @typedef { object } RuntimeInfo\r\n * @property { string } architecture The runtime build architecture\r\n * @property { string } manifestUrl The runtime manifest URL\r\n * @property { nubmer } port The runtime websocket port\r\n * @property { string } securityRealm The runtime security realm\r\n * @property { string } version The runtime version\r\n */\r\n/**\r\n * RVMInfo interface\r\n * @typedef { object } RVMInfo\r\n * @property { string } action The name of action: \"get-rvm-info\"\r\n * @property { string } appLogDirectory The app log directory\r\n * @property { string } path The path of OpenfinRVM.exe\r\n * @property { string } 'start-time' The start time of RVM\r\n * @property { string } version The version of RVM\r\n * @property { string } 'working-dir' The working directory\r\n */\r\n/**\r\n * ShortCutConfig interface\r\n * @typedef { object } ShortCutConfig\r\n * @property { boolean } desktop true if application has a shortcut on the desktop\r\n * @property { boolean } startMenu true if application has shortcut in the start menu\r\n * @property { boolean } systemStartup true if application will be launched on system startup\r\n */\r\n/**\r\n * SubOptions interface\r\n * @typedef { Object } SubOptions\r\n * @property { number } timestamp The event timestamp\r\n */\r\n/**\r\n * TaskBar interface\r\n * @typedef { object } TaskBar\r\n * @property { string } edge which edge of a monitor the taskbar is on\r\n * @property { Rect } rect The taskbar coordinates\r\n */\r\n/**\r\n * TerminateExternalRequestType interface\r\n * @typedef { object } TerminateExternalRequestType\r\n * @property { string } uuid The uuid of the running application\r\n * @property { number } timeout Time out period before the running application terminates\r\n * @property { boolean } killtree Value to terminate the running application\r\n */\r\n/**\r\n * Time interface\r\n * @typedef { object } Time\r\n * @property { number } user The number of milliseconds the CPU has spent in user mode\r\n * @property { number } nice The number of milliseconds the CPU has spent in nice mode\r\n * @property { number } sys The number of milliseconds the CPU has spent in sys mode\r\n * @property { number } idle The number of milliseconds the CPU has spent in idle mode\r\n * @property { number } irq The number of milliseconds the CPU has spent in irq mode\r\n */\r\n/**\r\n * TrayInfo interface\r\n * @typedef { object } TrayInfo\r\n * @property { Bounds } bounds The bound of tray icon in virtual screen pixels\r\n * @property { MonitorInfo } monitorInfo Please see fin.System.getMonitorInfo for more information\r\n * @property { number } x copy of bounds.x\r\n * @property { number } y copy of bounds.y\r\n */\r\n/**\r\n * WindowDetail interface\r\n * @typedef { object } WindowDetail\r\n * @property { number } bottom The bottom-most coordinate of the window\r\n * @property { number } height The height of the window\r\n * @property { boolean } isShowing Value to check if the window is showing\r\n * @property { number } left The left-most coordinate of the window\r\n * @property { string } name The name of the window\r\n * @property { number } right The right-most coordinate of the window\r\n * @property { string } state The window state\r\n * @property { number } top The top-most coordinate of the window\r\n * @property { number } width The width of the window\r\n */\r\n/**\r\n * WindowInfo interface\r\n * @typedef { object } WindowInfo\r\n * @property { Array } childWindows The array of child windows details\r\n * @property { WindowDetail } mainWindow The main window detail\r\n * @property { string } uuid The uuid of the application\r\n */\r\n/**\r\n * An object representing the core of OpenFin Runtime. Allows the developer\r\n * to perform system-level actions, such as accessing logs, viewing processes,\r\n * clearing the cache and exiting the runtime as well as listen to system events.\r\n * @namespace\r\n */\r\nclass System extends base_1.EmitterBase {\r\n constructor(wire) {\r\n super(wire, ['system']);\r\n }\r\n sendExternalProcessRequest(action, options) {\r\n return new Promise((resolve, reject) => {\r\n const exitEventKey = 'external-process-exited';\r\n let processUuid;\r\n let externalProcessExitHandler;\r\n let ofWindow;\r\n if (typeof options.listener === 'function') {\r\n externalProcessExitHandler = (payload) => {\r\n const data = payload || {};\r\n const exitPayload = {\r\n topic: 'exited',\r\n uuid: data.processUuid || '',\r\n exitCode: data.exitCode || 0\r\n };\r\n if (processUuid === payload.processUuid) {\r\n options.listener(exitPayload);\r\n ofWindow.removeListener(exitEventKey, externalProcessExitHandler);\r\n }\r\n };\r\n // window constructor expects the name is not undefined\r\n if (!this.wire.me.name) {\r\n this.wire.me.name = this.wire.me.uuid;\r\n }\r\n ofWindow = new window_1._Window(this.wire, this.wire.me);\r\n ofWindow.on(exitEventKey, externalProcessExitHandler);\r\n }\r\n this.wire.sendAction(action, options)\r\n .then(({ payload }) => {\r\n processUuid = payload.data.uuid;\r\n resolve(payload.data);\r\n }).catch((err) => {\r\n if (ofWindow) {\r\n ofWindow.removeListener(exitEventKey, externalProcessExitHandler);\r\n }\r\n reject(err);\r\n });\r\n });\r\n }\r\n /**\r\n * Adds a listener to the end of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - Called whenever an event of the specified type occurs.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function addListener\r\n * @memberof System\r\n * @instance\r\n * @tutorial System.EventEmitter\r\n */\r\n /**\r\n * Adds a listener to the end of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - Called whenever an event of the specified type occurs.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function on\r\n * @memberof System\r\n * @instance\r\n * @tutorial System.EventEmitter\r\n */\r\n /**\r\n * Adds a one time listener for the event. The listener is invoked only the first time the event is fired, after which it is removed.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function once\r\n * @memberof System\r\n * @instance\r\n * @tutorial System.EventEmitter\r\n */\r\n /**\r\n * Adds a listener to the beginning of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function prependListener\r\n * @memberof System\r\n * @instance\r\n * @tutorial System.EventEmitter\r\n */\r\n /**\r\n * Adds a one time listener for the event. The listener is invoked only the first time the event is fired, after which it is removed.\r\n * The listener is added to the beginning of the listeners array.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function prependOnceListener\r\n * @memberof System\r\n * @instance\r\n * @tutorial System.EventEmitter\r\n */\r\n /**\r\n * Remove a listener from the listener array for the specified event.\r\n * Caution: Calling this method changes the array indices in the listener array behind the listener.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function removeListener\r\n * @memberof System\r\n * @instance\r\n * @tutorial System.EventEmitter\r\n */\r\n /**\r\n * Removes all listeners, or those of the specified event.\r\n * @param { string | symbol } [eventType] - The type of the event.\r\n * @return {Promise.}\r\n * @function removeAllListeners\r\n * @memberof System\r\n * @instance\r\n * @tutorial System.EventEmitter\r\n */\r\n /**\r\n * Returns the version of the runtime. The version contains the major, minor,\r\n * build and revision numbers.\r\n * @return {Promise.}\r\n * @tutorial System.getVersion\r\n */\r\n getVersion() {\r\n return this.wire.sendAction('get-version')\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Clears cached data containing application resource\r\n * files (images, HTML, JavaScript files), cookies, and items stored in the\r\n * Local Storage.\r\n * @param { ClearCacheOption } options - See tutorial for more details.\r\n * @return {Promise.}\r\n * @tutorial System.clearCache\r\n */\r\n clearCache(options) {\r\n return this.wire.sendAction('clear-cache', options).then(() => undefined);\r\n }\r\n /**\r\n * Clears all cached data when OpenFin Runtime exits.\r\n * @return {Promise.}\r\n * @tutorial System.deleteCacheOnExit\r\n */\r\n deleteCacheOnExit() {\r\n return this.wire.sendAction('delete-cache-request').then(() => undefined);\r\n }\r\n /**\r\n * Exits the Runtime.\r\n * @return {Promise.}\r\n * @tutorial System.exit\r\n */\r\n exit() {\r\n return this.wire.sendAction('exit-desktop').then(() => undefined);\r\n }\r\n /**\r\n * Writes any unwritten cookies data to disk.\r\n * @return {Promise.}\r\n * @tutorial System.flushCookieStore\r\n */\r\n flushCookieStore() {\r\n return this.wire.sendAction('flush-cookie-store').then(() => undefined);\r\n }\r\n /**\r\n * Retrieves an array of data (name, ids, bounds) for all application windows.\r\n * @return {Promise.Array.}\r\n * @tutorial System.getAllWindows\r\n */\r\n getAllWindows() {\r\n return this.wire.sendAction('get-all-windows')\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves an array of data for all applications.\r\n * @return {Promise.Array.}\r\n * @tutorial System.getAllApplications\r\n */\r\n getAllApplications() {\r\n return this.wire.sendAction('get-all-applications')\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves the command line argument string that started OpenFin Runtime.\r\n * @return {Promise.}\r\n * @tutorial System.getCommandLineArguments\r\n */\r\n getCommandLineArguments() {\r\n return this.wire.sendAction('get-command-line-arguments')\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Get the current state of the crash reporter.\r\n * @return {Promise.}\r\n * @tutorial System.getCrashReporterState\r\n */\r\n getCrashReporterState() {\r\n return this.wire.sendAction('get-crash-reporter-state').then(({ payload }) => payload.data);\r\n }\r\n /* <-- Note the one asterisk to hide from jsdoc because we don't want to publish this method anymore.\r\n * @deprecated Use {@link System.getMachineId} instead.\r\n */\r\n getDeviceId() {\r\n console.warn('Function is deprecated; use getMachineId instead.');\r\n return this.wire.sendAction('get-device-id').then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Start the crash reporter for the browser process if not already running.\r\n * You can optionally specify `diagnosticMode` to have the logs sent to\r\n * OpenFin on runtime close\r\n *\r\n * @param { CrashReporterOption } options - configure crash reporter\r\n * @return {Promise.}\r\n * @tutorial System.startCrashReporter\r\n */\r\n startCrashReporter(options) {\r\n return new Promise((resolve, reject) => {\r\n if (!options.diagnosticMode) {\r\n return reject(new Error('diagnosticMode not found in options'));\r\n }\r\n this.wire.sendAction('start-crash-reporter', options).then(({ payload }) => resolve(payload.data)).catch(err => reject(err));\r\n });\r\n }\r\n /**\r\n * Returns a hex encoded hash of the mac address and the currently logged in user name\r\n * @return {Promise.}\r\n * @tutorial System.getDeviceUserId\r\n */\r\n getDeviceUserId() {\r\n return this.wire.sendAction('get-device-user-id').then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves a frame info object for the uuid and name passed in\r\n * @param { string } uuid - The UUID of the target.\r\n * @param { string } name - The name of the target.\r\n * @return {Promise.}\r\n * @tutorial System.getEntityInfo\r\n */\r\n getEntityInfo(uuid, name) {\r\n return this.wire.sendAction('get-entity-info', { uuid, name }).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Gets the value of a given environment variable on the computer on which the runtime is installed\r\n * @return {Promise.}\r\n * @tutorial System.getEnvironmentVariable\r\n */\r\n getEnvironmentVariable(envName) {\r\n return this.wire.sendAction('get-environment-variable', {\r\n environmentVariables: envName\r\n })\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Get current focused window.\r\n * @return {Promise.}\r\n * @tutorial System.getFocusedWindow\r\n */\r\n getFocusedWindow() {\r\n return this.wire.sendAction('get-focused-window').then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves the contents of the log with the specified filename.\r\n * @param { GetLogRequestType } options A object that id defined by the GetLogRequestType interface\r\n * @return {Promise.}\r\n * @tutorial System.getLog\r\n */\r\n getLog(options) {\r\n return this.wire.sendAction('view-log', options)\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Returns a unique identifier (UUID) provided by the machine.\r\n * @return {Promise.}\r\n * @tutorial System.getMachineId\r\n */\r\n getMachineId() {\r\n return this.wire.sendAction('get-machine-id').then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Returns the minimum (inclusive) logging level that is currently being written to the log.\r\n * @return {Promise.}\r\n * @tutorial System.getMinLogLevel\r\n */\r\n getMinLogLevel() {\r\n return this.wire.sendAction('get-min-log-level').then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves an array containing information for each log file.\r\n * @return {Promise.Array}\r\n * @tutorial System.getLogList\r\n */\r\n getLogList() {\r\n return this.wire.sendAction('list-logs')\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves an object that contains data about the monitor setup of the\r\n * computer that the runtime is running on.\r\n * @return {Promise.}\r\n * @tutorial System.getMonitorInfo\r\n */\r\n getMonitorInfo() {\r\n return this.wire.sendAction('get-monitor-info')\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Returns the mouse in virtual screen coordinates (left, top).\r\n * @return {Promise.}\r\n * @tutorial System.getMousePosition\r\n */\r\n getMousePosition() {\r\n return this.wire.sendAction('get-mouse-position')\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves an array of all of the runtime processes that are currently\r\n * running. Each element in the array is an object containing the uuid\r\n * and the name of the application to which the process belongs.\r\n * @return {Promise.Array.}\r\n * @tutorial System.getProcessList\r\n */\r\n getProcessList() {\r\n return this.wire.sendAction('process-snapshot')\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves the Proxy settings.\r\n * @return {Promise.}\r\n * @tutorial System.getProxySettings\r\n */\r\n getProxySettings() {\r\n return this.wire.sendAction('get-proxy-settings')\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Returns information about the running Runtime in an object.\r\n * @return {Promise.}\r\n * @tutorial System.getRuntimeInfo\r\n */\r\n getRuntimeInfo() {\r\n return this.wire.sendAction('get-runtime-info').then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Returns information about the running RVM in an object.\r\n * @return {Promise.}\r\n * @tutorial System.getRvmInfo\r\n */\r\n // incompatible with standalone node process.\r\n getRvmInfo() {\r\n return this.wire.sendAction('get-rvm-info')\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves system information.\r\n * @return {Promise.}\r\n * @tutorial System.getHostSpecs\r\n */\r\n getHostSpecs() {\r\n return this.wire.sendAction('get-host-specs').then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Runs an executable or batch file.\r\n * @param { ExternalProcessRequestType } options A object that is defined in the ExternalProcessRequestType interface\r\n * @return {Promise.}\r\n * @tutorial System.launchExternalProcess\r\n */\r\n launchExternalProcess(options) {\r\n return this.sendExternalProcessRequest('launch-external-process', options);\r\n }\r\n /**\r\n * Monitors a running process.\r\n * @param { ExternalProcessInfo } options See tutorial for more details\r\n * @return {Promise.}\r\n * @tutorial System.monitorExternalProcess\r\n */\r\n monitorExternalProcess(options) {\r\n return this.sendExternalProcessRequest('monitor-external-process', options);\r\n }\r\n /**\r\n * Writes the passed message into both the log file and the console.\r\n * @param { string } level The log level for the entry. Can be either \"info\", \"warning\" or \"error\"\r\n * @param { string } message The log message text\r\n * @return {Promise.}\r\n * @tutorial System.log\r\n */\r\n log(level, message) {\r\n return this.wire.sendAction('write-to-log', { level, message }).then(() => undefined);\r\n }\r\n /**\r\n * Opens the passed URL in the default web browser.\r\n * @param { string } url The URL to open\r\n * @return {Promise.}\r\n * @tutorial System.openUrlWithBrowser\r\n */\r\n openUrlWithBrowser(url) {\r\n return this.wire.sendAction('open-url-with-browser', { url }).then(() => undefined);\r\n }\r\n /**\r\n * Removes the process entry for the passed UUID obtained from a prior call\r\n * of fin.System.launchExternalProcess().\r\n * @param { string } uuid The UUID for a process obtained from a prior call to fin.desktop.System.launchExternalProcess()\r\n * @return {Promise.}\r\n * @tutorial System.releaseExternalProcess\r\n */\r\n releaseExternalProcess(uuid) {\r\n return this.wire.sendAction('release-external-process', { uuid }).then(() => undefined);\r\n }\r\n /**\r\n * Shows the Chromium Developer Tools for the specified window\r\n * @param { Identity } identity This is a object that is defined by the Identity interface\r\n * @return {Promise.}\r\n * @tutorial System.showDeveloperTools\r\n */\r\n showDeveloperTools(identity) {\r\n return this.wire.sendAction('show-developer-tools', identity).then(() => undefined);\r\n }\r\n /**\r\n * Attempt to close an external process. The process will be terminated if it\r\n * has not closed after the elapsed timeout in milliseconds.\r\n * @param { TerminateExternalRequestType } options A object defined in the TerminateExternalRequestType interface\r\n * @return {Promise.}\r\n * @tutorial System.terminateExternalProcess\r\n */\r\n terminateExternalProcess(options) {\r\n return this.wire.sendAction('terminate-external-process', options)\r\n .then(() => undefined);\r\n }\r\n /**\r\n * Update the OpenFin Runtime Proxy settings.\r\n * @param { ProxyConfig } options A config object defined in the ProxyConfig interface\r\n * @return {Promise.}\r\n * @tutorial System.updateProxySettings\r\n */\r\n updateProxySettings(options) {\r\n return this.wire.sendAction('update-proxy', options).then(() => undefined);\r\n }\r\n /**\r\n * Downloads the given application asset\r\n * @param { AppAssetInfo } appAsset App asset object\r\n * @return {Promise.}\r\n * @tutorial System.downloadAsset\r\n */\r\n // incompatible with standalone node process.\r\n downloadAsset(appAsset, progressListener) {\r\n return new Promise((resolve, reject) => {\r\n //node.js environment not supported\r\n if (this.wire.environment.constructor.name === 'NodeEnvironment') {\r\n reject(new transport_errors_1.NotSupportedError('downloadAsset only supported in an OpenFin Render process'));\r\n return;\r\n }\r\n const downloadId = this.wire.environment.getNextMessageId().toString();\r\n const dlProgressKey = `asset-download-progress-${downloadId}`;\r\n const dlErrorKey = `asset-download-error-${downloadId}`;\r\n const dlCompleteKey = `asset-download-complete-${downloadId}`;\r\n const dlProgress = (progress) => {\r\n const p = {\r\n downloadedBytes: progress.downloadedBytes,\r\n totalBytes: progress.totalBytes\r\n };\r\n progressListener(p);\r\n };\r\n const cleanListeners = () => {\r\n this.removeListener(dlProgressKey, dlProgress);\r\n };\r\n const dlError = (r, err) => {\r\n const error = err ? err : r;\r\n cleanListeners();\r\n reject(new transport_errors_1.RuntimeError(error));\r\n };\r\n const dlComplete = () => {\r\n cleanListeners();\r\n resolve();\r\n };\r\n this.on(dlProgressKey, dlProgress);\r\n this.once(dlErrorKey, dlError);\r\n this.once(dlCompleteKey, dlComplete);\r\n const downloadOptions = Object.assign(appAsset, { downloadId });\r\n this.wire.sendAction('download-asset', downloadOptions).catch((err) => {\r\n cleanListeners();\r\n reject(err);\r\n });\r\n });\r\n }\r\n /**\r\n * Downloads a version of the runtime.\r\n * @param { RuntimeDownloadOptions } options - Download options.\r\n * @param {Function} [progressListener] - called as the runtime is downloaded with progress information.\r\n * @return {Promise.}\r\n * @tutorial System.downloadRuntime\r\n */\r\n downloadRuntime(options, progressListener) {\r\n return new Promise((resolve, reject) => {\r\n //node.js environment not supported\r\n if (this.wire.environment.constructor.name === 'NodeEnvironment') {\r\n reject(new transport_errors_1.NotSupportedError('downloadRuntime only supported in an OpenFin Render process'));\r\n return;\r\n }\r\n const downloadId = this.wire.environment.getNextMessageId().toString();\r\n const dlProgressKey = `runtime-download-progress-${downloadId}`;\r\n const dlErrorKey = `runtime-download-error-${downloadId}`;\r\n const dlCompleteKey = `runtime-download-complete-${downloadId}`;\r\n const dlProgress = (progress) => {\r\n const p = {\r\n downloadedBytes: progress.downloadedBytes,\r\n totalBytes: progress.totalBytes\r\n };\r\n progressListener(p);\r\n };\r\n const cleanListeners = () => {\r\n this.removeListener(dlProgressKey, dlProgress);\r\n };\r\n const dlError = (r, err) => {\r\n const error = err ? err : r;\r\n cleanListeners();\r\n reject(new transport_errors_1.RuntimeError(error));\r\n };\r\n const dlComplete = () => {\r\n cleanListeners();\r\n resolve();\r\n };\r\n this.on(dlProgressKey, dlProgress);\r\n this.once(dlErrorKey, dlError);\r\n this.once(dlCompleteKey, dlComplete);\r\n const downloadOptions = Object.assign(options, { downloadId });\r\n this.wire.sendAction('download-runtime', downloadOptions).catch((err) => {\r\n cleanListeners();\r\n reject(err);\r\n });\r\n });\r\n }\r\n /**\r\n * Download preload scripts from given URLs\r\n * @param {DownloadPreloadOption[]} scripts - URLs of preload scripts. See tutorial for more details.\r\n * @return {Promise.Array}\r\n * @tutorial System.downloadPreloadScripts\r\n */\r\n downloadPreloadScripts(scripts) {\r\n return this.wire.sendAction('download-preload-scripts', { scripts }).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves an array of data (name, ids, bounds) for all application windows.\r\n * @return {Promise.Array.}\r\n * @tutorial System.getAllExternalApplications\r\n */\r\n getAllExternalApplications() {\r\n return this.wire.sendAction('get-all-external-applications')\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves app asset information.\r\n * @param { AppAssetRequest } options\r\n * @return {Promise.}\r\n * @tutorial System.getAppAssetInfo\r\n */\r\n getAppAssetInfo(options) {\r\n return this.wire.sendAction('get-app-asset-info', options).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Get additional info of cookies.\r\n * @param { CookieOption } options - See tutorial for more details.\r\n * @return {Promise.Array.}\r\n * @tutorial System.getCookies\r\n */\r\n getCookies(options) {\r\n return this.wire.sendAction('get-cookies', options).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Set the minimum log level above which logs will be written to the OpenFin log\r\n * @param { LogLevel } The minimum level (inclusive) above which all calls to log will be written\r\n * @return {Promise.}\r\n * @tutorial System.setMinLogLevel\r\n */\r\n setMinLogLevel(level) {\r\n return this.wire.sendAction('set-min-log-level', { level }).then(() => undefined);\r\n }\r\n /**\r\n * Retrieves the UUID of the computer on which the runtime is installed\r\n * @param { string } uuid The uuid of the running application\r\n * @return {Promise.}\r\n * @tutorial System.resolveUuid\r\n */\r\n resolveUuid(uuid) {\r\n return this.wire.sendAction('resolve-uuid', {\r\n entityKey: uuid\r\n }).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Retrieves an array of data for all external applications\r\n * @param { Identity } requestingIdentity This object is described in the Identity typedef\r\n * @param { any } data Any data type to pass to the method\r\n * @return {Promise.}\r\n * @ignore\r\n */\r\n executeOnRemote(requestingIdentity, data) {\r\n data.requestingIdentity = requestingIdentity;\r\n return this.wire.ferryAction(data);\r\n }\r\n /**\r\n * Reads the specifed value from the registry.\r\n * @param { string } rootKey - The registry root key.\r\n * @param { string } subkey - The registry key.\r\n * @param { string } value - The registry value name.\r\n * @return {Promise.}\r\n * @tutorial System.readRegistryValue\r\n */\r\n readRegistryValue(rootKey, subkey, value) {\r\n return this.wire.sendAction('read-registry-value', {\r\n rootKey: rootKey,\r\n subkey: subkey,\r\n value: value\r\n }).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * This function call will register a unique id and produce a token.\r\n * The token can be used to broker an external connection.\r\n * @param { string } uuid - A UUID for the remote connection.\r\n * @return {Promise.}\r\n * @tutorial System.registerExternalConnection\r\n */\r\n registerExternalConnection(uuid) {\r\n return this.wire.sendAction('register-external-connection', { uuid }).then(({ payload }) => payload.data);\r\n }\r\n}\r\nexports.default = System;\r\n\n\n//# sourceURL=webpack:///./src/api/system/system.ts?"); /***/ }), /***/ "./src/api/window/window.ts": /*!**********************************!*\ !*** ./src/api/window/window.ts ***! \**********************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst base_1 = __webpack_require__(/*! ../base */ \"./src/api/base.ts\");\r\nconst application_1 = __webpack_require__(/*! ../application/application */ \"./src/api/application/application.ts\");\r\n/**\r\n * @lends Window\r\n */\r\n// tslint:disable-next-line\r\nclass _WindowModule extends base_1.Base {\r\n /**\r\n * Asynchronously returns a Window object that represents an existing window.\r\n * @param { Identity } identity\r\n * @return {Promise.<_Window>}\r\n * @tutorial Window.wrap\r\n * @static\r\n */\r\n async wrap(identity) {\r\n return new _Window(this.wire, identity);\r\n }\r\n /**\r\n * Synchronously returns a Window object that represents an existing window.\r\n * @param { Identity } identity\r\n * @return {_Window}\r\n * @tutorial Window.wrapSync\r\n * @static\r\n */\r\n wrapSync(identity) {\r\n return new _Window(this.wire, identity);\r\n }\r\n /**\r\n * Creates a new Window.\r\n * @param { Window~options } options - Window creation options\r\n * @return {Promise.<_Window>}\r\n * @tutorial Window.create\r\n * @static\r\n */\r\n create(options) {\r\n const win = new _Window(this.wire, { uuid: this.me.uuid, name: options.name });\r\n return win.createWindow(options);\r\n }\r\n /**\r\n * Asynchronously returns a Window object that represents the current window\r\n * @return {Promise.<_Window>}\r\n * @tutorial Window.getCurrent\r\n * @static\r\n */\r\n getCurrent() {\r\n return this.wrap(this.wire.me);\r\n }\r\n /**\r\n * Synchronously returns a Window object that represents the current window\r\n * @return {_Window}\r\n * @tutorial Window.getCurrentSync\r\n * @static\r\n */\r\n getCurrentSync() {\r\n return this.wrapSync(this.wire.me);\r\n }\r\n}\r\nexports.default = _WindowModule;\r\n/**\r\n * @typedef {object} Window~options\r\n * @summary Window creation options.\r\n * @desc This is the options object required by {@link Window.create Window.create}.\r\n *\r\n * Note that `name` is the only required property — albeit the `url` property is usually provided as well\r\n * (defaults to `\"about:blank\"` when omitted).\r\n *\r\n * _This jsdoc typedef mirrors the `WindowOptions` TypeScript interface in `@types/openfin`._\r\n *\r\n * @property {object} [accelerator]\r\n * Enable keyboard shortcuts for devtools, zoom, reload, and reload ignoring cache.\r\n *\r\n * @property {boolean} [accelerator.devtools=false]\r\n * If `true`, enables the devtools keyboard shortcut:
\r\n * `Ctrl` + `Shift` + `I` _(Toggles Devtools)_\r\n *\r\n * @property {boolean} [accelerator.reload=false]\r\n * If `true`, enables the reload keyboard shortcuts:
\r\n * `Ctrl` + `R` _(Windows)_
\r\n * `F5` _(Windows)_
\r\n * `Command` + `R` _(Mac)_\r\n *\r\n * @property {boolean} [accelerator.reloadIgnoringCache=false]\r\n * If `true`, enables the reload-from-source keyboard shortcuts:
\r\n * `Ctrl` + `Shift` + `R` _(Windows)_
\r\n * `Shift` + `F5` _(Windows)_
\r\n * `Command` + `Shift` + `R` _(Mac)_\r\n *\r\n * @property {boolean} [accelerator.zoom=false]\r\n * If `true`, enables the zoom keyboard shortcuts:
\r\n * `Ctrl` + `+` _(Zoom In)_
\r\n * `Ctrl` + `Shift` + `+` _(Zoom In)_
\r\n * `Ctrl` + `-` _(Zoom Out)_
\r\n * `Ctrl` + `Shift` + `-` _(Zoom Out)_
\r\n * `Ctrl` + `Scroll` _(Zoom In & Out)_
\r\n * `Ctrl` + `0` _(Restore to 100%)_\r\n *\r\n * @property {boolean} [alwaysOnTop=false] - _Updatable._\r\n * A flag to always position the window at the top of the window stack.\r\n *\r\n * @property {object} [api]\r\n * Configurations for API injection.\r\n *\r\n * @property {object} [api.iframe] Configure if the the API should be injected into iframes based on domain.\r\n *\r\n * @property {boolean} [api.iframe.crossOriginInjection=false] Controls if the `fin` API object is present for cross origin iframes.\r\n * @property {boolean} [api.iframe.sameOriginInjection=true] Controls if the `fin` API object is present for same origin iframes.\r\n *\r\n * @property {number} [aspectRatio=0] - _Updatable._\r\n * The aspect ratio of width to height to enforce for the window. If this value is equal to or less than zero,\r\n * an aspect ratio will not be enforced.\r\n *\r\n * @property {boolean} [autoShow=true]\r\n * A flag to automatically show the window when it is created.\r\n *\r\n * @property {string} [backgroundColor=\"#FFF\"]\r\n * The window’s _backfill_ color as a hexadecimal value. Not to be confused with the content background color\r\n * (`document.body.style.backgroundColor`),\r\n * this color briefly fills a window’s (a) content area before its content is loaded as well as (b) newly exposed\r\n * areas when growing a window. Setting\r\n * this value to the anticipated content background color can help improve user experience.\r\n * Default is white.\r\n *\r\n * @property {object} [contentNavigation]\r\n * Restrict navigation to URLs that match a whitelisted pattern. See [here](https://developer.chrome.com/extensions/match_patterns)\r\n * for more details.\r\n * @property {string[]} [contentNavigation.whitelist=[]] List of whitelisted URLs.\r\n *\r\n * @property {boolean} [contextMenu=true] - _Updatable._\r\n * A flag to show the context menu when right-clicking on a window.\r\n * Gives access to the devtools for the window.\r\n *\r\n * @property {object} [cornerRounding] - _Updatable._\r\n * Defines and applies rounded corners for a frameless window. **NOTE:** On macOS corner is not ellipse but circle rounded by the\r\n * average of _height_ and _width_.\r\n * @property {number} [cornerRounding.height=0] The height in pixels.\r\n * @property {number} [cornerRounding.width=0] The width in pixels.\r\n *\r\n * @property {string} [customData=\"\"] - _Updatable._\r\n * A field that the user can attach serializable data to to be ferried around with the window options.\r\n * _When omitted, the default value of this property is the empty string (`\"\"`)._\r\n *\r\n * @property {customRequestHeaders[]} [customRequestHeaders]\r\n * Defines list of {@link customRequestHeaders} for requests sent by the window.\r\n *\r\n * @property {boolean} [defaultCentered=false]\r\n * Centers the window in the primary monitor. This option overrides `defaultLeft` and `defaultTop`. When `saveWindowState` is `true`,\r\n * this value will be ignored for subsequent launches in favor of the cached value. **NOTE:** On macOS _defaultCenter_ is\r\n * somewhat above center vertically.\r\n *\r\n * @property {number} [defaultHeight=500]\r\n * The default height of the window. When `saveWindowState` is `true`, this value will be ignored for subsequent launches\r\n * in favor of the cached value.\r\n *\r\n * @property {number} [defaultLeft=100]\r\n * The default left position of the window. When `saveWindowState` is `true`, this value will be ignored for subsequent\r\n * launches in favor of the cached value.\r\n *\r\n * @property {number} [defaultTop=100]\r\n * The default top position of the window. When `saveWindowState` is `true`, this value will be ignored for subsequent\r\n * launches in favor of the cached value.\r\n *\r\n * @property {number} [defaultWidth=800]\r\n * The default width of the window. When `saveWindowState` is `true`, this value will be ignored for subsequent\r\n * launches in favor of the cached value.\r\n *\r\n * @property {boolean} [frame=true] - _Updatable._\r\n * A flag to show the frame.\r\n *\r\n * @hidden-property {boolean} [hideOnClose=false] - A flag to allow a window to be hidden when the close button is clicked.\r\n *\r\n * @property {string} [icon] - _Updatable. Inheritable._\r\n * A URL for the icon to be shown in the window title bar and the taskbar.\r\n * _When omitted, inherits from the parent application._\r\n *\r\n * @property {number} [maxHeight=-1] - _Updatable._\r\n * The maximum height of a window. Will default to the OS defined value if set to -1.\r\n *\r\n * @property {boolean} [maximizable=true] - _Updatable._\r\n * A flag that lets the window be maximized.\r\n *\r\n * @property {number} [maxWidth=-1] - _Updatable._\r\n * The maximum width of a window. Will default to the OS defined value if set to -1.\r\n *\r\n * @property {number} [minHeight=0] - _Updatable._\r\n * The minimum height of a window.\r\n *\r\n * @property {boolean} [minimizable=true] - _Updatable._\r\n * A flag that lets the window be minimized.\r\n *\r\n * @property {number} [minWidth=0] - _Updatable._\r\n * The minimum width of a window.\r\n *\r\n * @property {string} name\r\n * The name of the window.\r\n *\r\n * @property {number} [opacity=1.0] - _Updatable._\r\n * A flag that specifies how transparent the window will be.\r\n * This value is clamped between `0.0` and `1.0`.\r\n *\r\n * @property {preloadScript[]} [preloadScripts] - _Inheritable_\r\n * A list of scripts that are eval'ed before other scripts in the page. When omitted, _inherits_\r\n * from the parent application.\r\n *\r\n * @property {boolean} [resizable=true] - _Updatable._\r\n * A flag to allow the user to resize the window.\r\n *\r\n * @property {object} [resizeRegion] - _Updatable._\r\n * Defines a region in pixels that will respond to user mouse interaction for resizing a frameless window.\r\n * @property {number} [resizeRegion.bottomRightCorner=9]\r\n * The size in pixels of an additional square resizable region located at the bottom right corner of a frameless window.\r\n * @property {number} [resizeRegion.size=7]\r\n * The size in pixels.\r\n * @property {object} [resizeRegion.sides={top:true,right:true,bottom:true,left:true}]\r\n * Sides that a window can be resized from.\r\n *\r\n * @property {boolean} [saveWindowState=true]\r\n * A flag to cache the location of the window.\r\n *\r\n * @property {boolean} [shadow=false]\r\n * A flag to display a shadow on frameless windows.\r\n * `shadow` and `cornerRounding` are mutually exclusive.\r\n * On Windows 7, Aero theme is required.\r\n *\r\n * @property {boolean} [showTaskbarIcon=true] - _Updatable._ _Windows_.\r\n * A flag to show the window's icon in the taskbar.\r\n *\r\n * @property {boolean} [smallWindow=false]\r\n * A flag to specify a frameless window that can be be created and resized to less than 41x36px (width x height).\r\n * _Note: Caveats of small windows are no Aero Snap and drag to/from maximize._\r\n *\r\n * @property {string} [state=\"normal\"]\r\n * The visible state of the window on creation.\r\n * One of:\r\n * * `\"maximized\"`\r\n * * `\"minimized\"`\r\n * * `\"normal\"`\r\n *\r\n * @property {string} [taskbarIconGroup=] - _Windows_.\r\n * Specify a taskbar group for the window.\r\n * _If omitted, defaults to app's uuid (`fin.desktop.Application.getCurrent().uuid`)._\r\n *\r\n * @property {string} [url=\"about:blank\"]\r\n * The URL of the window.\r\n *\r\n * @property {string} [uuid=]\r\n * The `uuid` of the application, unique within the set of all `Application`s running in OpenFin Runtime.\r\n * If omitted, defaults to the `uuid` of the application spawning the window.\r\n * If given, must match the `uuid` of the application spawning the window.\r\n * In other words, the application's `uuid` is the only acceptable value, but is the default, so there's\r\n * really no need to provide it.\r\n *\r\n * @property {boolean} [waitForPageLoad=false]\r\n * When set to `true`, the window will not appear until the `window` object's `load` event fires.\r\n * When set to `false`, the window will appear immediately without waiting for content to be loaded.\r\n */\r\n/**\r\n * @typedef { Object } Area\r\n * @property { number } height Area's height\r\n * @property { number } width Area's width\r\n * @property { number } x X coordinate of area's starting point\r\n * @property { number } y Y coordinate of area's starting point\r\n */\r\n/**\r\n * @typedef {object} Transition\r\n * @property {Opacity} opacity - The Opacity transition\r\n * @property {Position} position - The Position transition\r\n * @property {Size} size - The Size transition\r\n*/\r\n/**\r\n * @typedef {object} TransitionOptions\r\n * @property {boolean} interrupt - This option interrupts the current animation. When false it pushes\r\nthis animation onto the end of the animation queue.\r\n * @property {boolean} relative - Treat 'opacity' as absolute or as a delta. Defaults to false.\r\n */\r\n/**\r\n * @typedef {object} Size\r\n * @property {number} duration - The total time in milliseconds this transition should take.\r\n * @property {boolean} relative - Treat 'opacity' as absolute or as a delta. Defaults to false.\r\n * @property {number} width - Optional if height is present. Defaults to the window's current width.\r\n * @property {number} height - Optional if width is present. Defaults to the window's current height.\r\n */\r\n/**\r\n * @typedef {object} Position\r\n * @property {number} duration - The total time in milliseconds this transition should take.\r\n * @property {boolean} relative - Treat 'opacity' as absolute or as a delta. Defaults to false.\r\n * @property {number} left - Defaults to the window's current left position in virtual screen coordinates.\r\n * @property {number} top - Defaults to the window's current top position in virtual screen coordinates.\r\n */\r\n/**\r\n * @typedef {object} Opacity\r\n * @property {number} duration - The total time in milliseconds this transition should take.\r\n * @property {boolean} relative - Treat 'opacity' as absolute or as a delta. Defaults to false.\r\n * @property {number} opacity - This value is clamped from 0.0 to 1.0.\r\n*/\r\n/**\r\n * Bounds is a interface that has the properties of height,\r\n * width, left, top which are all numbers\r\n * @typedef { object } Bounds\r\n * @property { number } height Get the application height bound\r\n * @property { number } width Get the application width bound\r\n * @property { number } top Get the application top bound\r\n * @property { number } left Get the application left bound\r\n * @property { number } right Get the application right bound\r\n * @property { number } bottom Get the application bottom bound\r\n */\r\n/**\r\n * @classdesc A basic window that wraps a native HTML window. Provides more fine-grained\r\n * control over the window state such as the ability to minimize, maximize, restore, etc.\r\n * By default a window does not show upon instantiation; instead the window's show() method\r\n * must be invoked manually. The new window appears in the same process as the parent window.\r\n * It has the ability to listen for window specific events.\r\n * @class\r\n * @alias Window\r\n * @hideconstructor\r\n */\r\n// The window.Window name is taken\r\n// tslint:disable-next-line\r\nclass _Window extends base_1.EmitterBase {\r\n constructor(wire, identity) {\r\n super(wire, ['window', identity.uuid, identity.name]);\r\n this.identity = identity;\r\n }\r\n /**\r\n * Adds a listener to the end of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - Called whenever an event of the specified type occurs.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function addListener\r\n * @memberof Window\r\n * @instance\r\n * @tutorial Window.EventEmitter\r\n */\r\n /**\r\n * Adds a listener to the end of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - Called whenever an event of the specified type occurs.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function on\r\n * @memberof Window\r\n * @instance\r\n * @tutorial Window.EventEmitter\r\n */\r\n /**\r\n * Adds a one time listener for the event. The listener is invoked only the first time the event is fired, after which it is removed.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function once\r\n * @memberof Window\r\n * @instance\r\n * @tutorial Window.EventEmitter\r\n */\r\n /**\r\n * Adds a listener to the beginning of the listeners array for the specified event.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function prependListener\r\n * @memberof Window\r\n * @instance\r\n * @tutorial Window.EventEmitter\r\n */\r\n /**\r\n * Adds a one time listener for the event. The listener is invoked only the first time the event is fired, after which it is removed.\r\n * The listener is added to the beginning of the listeners array.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function prependOnceListener\r\n * @memberof Window\r\n * @instance\r\n * @tutorial Window.EventEmitter\r\n */\r\n /**\r\n * Remove a listener from the listener array for the specified event.\r\n * Caution: Calling this method changes the array indices in the listener array behind the listener.\r\n * @param { string | symbol } eventType - The type of the event.\r\n * @param { Function } listener - The callback function.\r\n * @param { SubOptions } [options] - Option to support event timestamps.\r\n * @return {Promise.}\r\n * @function removeListener\r\n * @memberof Window\r\n * @instance\r\n * @tutorial Window.EventEmitter\r\n */\r\n /**\r\n * Removes all listeners, or those of the specified event.\r\n * @param { string | symbol } [eventType] - The type of the event.\r\n * @return {Promise.}\r\n * @function removeAllListeners\r\n * @memberof Window\r\n * @instance\r\n * @tutorial Window.EventEmitter\r\n */\r\n // create a new window\r\n createWindow(options) {\r\n return new Promise((resolve, reject) => {\r\n const CONSTRUCTOR_CB_TOPIC = 'fire-constructor-callback';\r\n // need to call pageResponse, otherwise when a child window is created, page is not loaded\r\n const pageResponse = new Promise((resolve) => {\r\n // tslint:disable-next-line\r\n this.on(CONSTRUCTOR_CB_TOPIC, function fireConstructor(response) {\r\n let cbPayload;\r\n const success = response.success;\r\n const responseData = response.data;\r\n const message = responseData.message;\r\n if (success) {\r\n cbPayload = {\r\n httpResponseCode: responseData.httpResponseCode,\r\n apiInjected: responseData.apiInjected\r\n };\r\n }\r\n else {\r\n cbPayload = {\r\n message: responseData.message,\r\n networkErrorCode: responseData.networkErrorCode,\r\n stack: responseData.stack\r\n };\r\n }\r\n this.removeListener(CONSTRUCTOR_CB_TOPIC, fireConstructor);\r\n resolve({\r\n message: message,\r\n cbPayload: cbPayload,\r\n success: success\r\n });\r\n });\r\n });\r\n const windowCreation = this.wire.environment.createChildWindow(options);\r\n Promise.all([pageResponse, windowCreation]).then((resolvedArr) => {\r\n const pageResolve = resolvedArr[0];\r\n if (pageResolve.success) {\r\n resolve(this);\r\n }\r\n else {\r\n reject(pageResolve);\r\n }\r\n });\r\n });\r\n }\r\n windowListFromNameList(identityList) {\r\n const windowList = [];\r\n identityList.forEach(identity => {\r\n windowList.push(new _Window(this.wire, {\r\n uuid: identity.uuid,\r\n name: identity.name\r\n }));\r\n });\r\n return windowList;\r\n }\r\n /**\r\n * Retrieves an array of frame info objects representing the main frame and any\r\n * iframes that are currently on the page.\r\n * @return {Promise.>}\r\n * @tutorial Window.getAllFrames\r\n */\r\n getAllFrames() {\r\n return this.wire.sendAction('get-all-frames', this.identity).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Gets the current bounds (top, left, width, height) of the window.\r\n * @return {Promise.}\r\n * @tutorial Window.getBounds\r\n */\r\n getBounds() {\r\n return this.wire.sendAction('get-window-bounds', this.identity)\r\n // tslint:disable-next-line\r\n .then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Gives focus to the window.\r\n * @return {Promise.}\r\n * @emits _Window#focused\r\n * @tutorial Window.focus\r\n */\r\n focus() {\r\n return this.wire.sendAction('focus-window', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Removes focus from the window.\r\n * @return {Promise.}\r\n * @tutorial Window.blur\r\n */\r\n blur() {\r\n return this.wire.sendAction('blur-window', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Brings the window to the front of the window stack.\r\n * @return {Promise.}\r\n * @tutorial Window.bringToFront\r\n */\r\n bringToFront() {\r\n return this.wire.sendAction('bring-window-to-front', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Performs the specified window transitions.\r\n * @param {Transition} transitions - Describes the animations to perform. See the tutorial.\r\n * @param {TransitionOptions} options - Options for the animation. See the tutorial.\r\n * @return {Promise.}\r\n * @tutorial Window.animate\r\n */\r\n animate(transitions, options) {\r\n return this.wire.sendAction('animate-window', Object.assign({}, this.identity, {\r\n transitions,\r\n options\r\n })).then(() => undefined);\r\n }\r\n /**\r\n * Hides the window.\r\n * @return {Promise.}\r\n * @tutorial Window.hide\r\n */\r\n hide() {\r\n return this.wire.sendAction('hide-window', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * closes the window application\r\n * @param { boolean } [force = false] Close will be prevented from closing when force is false and\r\n * â€close-requested’ has been subscribed to for application’s main window.\r\n * @return {Promise.}\r\n * @tutorial Window.close\r\n */\r\n close(force = false) {\r\n return this.wire.sendAction('close-window', Object.assign({}, this.identity, { force }))\r\n .then(() => {\r\n Object.setPrototypeOf(this, null);\r\n return undefined;\r\n });\r\n }\r\n /**\r\n * Returns the native OS level Id.\r\n * In Windows, it will return the Windows [handle](https://docs.microsoft.com/en-us/windows/desktop/WinProg/windows-data-types#HWND).\r\n * @return {Promise.}\r\n * @tutorial Window.getNativeId\r\n */\r\n getNativeId() {\r\n return this.wire.sendAction('get-window-native-id', this.identity)\r\n .then(({ payload }) => payload.data);\r\n }\r\n /*\r\n * @deprecated Use {@link Window.disableUserMovement} instead.\r\n */\r\n disableFrame() {\r\n console.warn('Function is deprecated; use disableUserMovement instead.');\r\n return this.wire.sendAction('disable-window-frame', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Prevents a user from changing a window's size/position when using the window's frame.\r\n * @return {Promise.}\r\n * @tutorial Window.disableUserMovement\r\n */\r\n disableUserMovement() {\r\n return this.wire.sendAction('disable-window-frame', this.identity).then(() => undefined);\r\n }\r\n /*\r\n * @deprecated Use {@link Window.enableUserMovement} instead.\r\n */\r\n enableFrame() {\r\n console.warn('Function is deprecated; use enableUserMovement instead.');\r\n return this.wire.sendAction('enable-window-frame', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Re-enables user changes to a window's size/position when using the window's frame.\r\n * @return {Promise.}\r\n * @tutorial Window.enableUserMovement\r\n */\r\n enableUserMovement() {\r\n return this.wire.sendAction('enable-window-frame', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Executes Javascript on the window, restricted to windows you own or windows owned by\r\n * applications you have created.\r\n * @param { string } code JavaScript code to be executed on the window.\r\n * @return {Promise.}\r\n * @tutorial Window.executeJavaScript\r\n */\r\n executeJavaScript(code) {\r\n return this.wire.sendAction('execute-javascript-in-window', Object.assign({}, this.identity, { code }))\r\n .then(() => undefined);\r\n }\r\n /**\r\n * Flashes the window’s frame and taskbar icon until stopFlashing is called.\r\n * @return {Promise.}\r\n * @tutorial Window.flash\r\n */\r\n flash() {\r\n return this.wire.sendAction('flash-window', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Stops the taskbar icon from flashing.\r\n * @return {Promise.}\r\n * @tutorial Window.stopFlashing\r\n */\r\n stopFlashing() {\r\n return this.wire.sendAction('stop-flash-window', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Retrieves an array containing wrapped fin.desktop.Windows that are grouped with this\r\n * window. If a window is not in a group an empty array is returned. Please note that\r\n * calling window is included in the result array.\r\n * @return {Promise.>}\r\n * @tutorial Window.getGroup\r\n */\r\n getGroup() {\r\n return this.wire.sendAction('get-window-group', Object.assign({}, this.identity, {\r\n crossApp: true // cross app group supported\r\n })).then(({ payload }) => {\r\n // tslint:disable-next-line\r\n let winGroup = [];\r\n if (payload.data.length) {\r\n winGroup = this.windowListFromNameList(payload.data);\r\n }\r\n return winGroup;\r\n });\r\n }\r\n /**\r\n * Gets an information object for the window.\r\n * @return {Promise.}\r\n * @tutorial Window.getInfo\r\n */\r\n getInfo() {\r\n return this.wire.sendAction('get-window-info', this.identity).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Gets the current settings of the window.\r\n * @return {Promise.}\r\n * @tutorial Window.getOptions\r\n */\r\n getOptions() {\r\n return this.wire.sendAction('get-window-options', this.identity).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Gets the parent application.\r\n * @return {Promise.}\r\n * @tutorial Window.getParentApplication\r\n */\r\n getParentApplication() {\r\n return Promise.resolve(new application_1.Application(this.wire, this.identity));\r\n }\r\n /**\r\n * Gets the parent window.\r\n * @return {Promise.<_Window>}\r\n * @tutorial Window.getParentWindow\r\n */\r\n getParentWindow() {\r\n return Promise.resolve(new application_1.Application(this.wire, this.identity)).then(app => app.getWindow());\r\n }\r\n /**\r\n * Gets a base64 encoded PNG snapshot of the window or just part a of it.\r\n * @param { Area } [area] The area of the window to be captured.\r\n * Omitting it will capture the whole visible window.\r\n * @return {Promise.}\r\n * @tutorial Window.getSnapshot\r\n */\r\n async getSnapshot(area) {\r\n const req = Object.assign({}, this.identity, { area });\r\n const res = await this.wire.sendAction('get-window-snapshot', req);\r\n return res.payload.data;\r\n }\r\n /**\r\n * Gets the current state (\"minimized\", \"maximized\", or \"restored\") of the window.\r\n * @return {Promise.}\r\n * @tutorial Window.getState\r\n */\r\n getState() {\r\n return this.wire.sendAction('get-window-state', this.identity).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Determines if the window is a main window.\r\n * @return {boolean}\r\n * @tutorial Window.isMainWindow\r\n */\r\n isMainWindow() {\r\n return this.me.uuid === this.me.name;\r\n }\r\n /**\r\n * Determines if the window is currently showing.\r\n * @return {Promise.}\r\n * @tutorial Window.isShowing\r\n */\r\n isShowing() {\r\n return this.wire.sendAction('is-window-showing', this.identity).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Joins the same window group as the specified window.\r\n * @param { _Window } target The window whose group is to be joined\r\n * @return {Promise.}\r\n * @tutorial Window.joinGroup\r\n */\r\n joinGroup(target) {\r\n return this.wire.sendAction('join-window-group', Object.assign({}, this.identity, {\r\n groupingUuid: target.identity.uuid,\r\n groupingWindowName: target.identity.name\r\n })).then(() => undefined);\r\n }\r\n /**\r\n * Reloads the window current page\r\n * @return {Promise.}\r\n * @tutorial Window.reload\r\n */\r\n reload(ignoreCache = false) {\r\n return this.wire.sendAction('reload-window', Object.assign({}, this.identity, {\r\n ignoreCache\r\n })).then(() => undefined);\r\n }\r\n /**\r\n * Leaves the current window group so that the window can be move independently of those in the group.\r\n * @return {Promise.}\r\n * @tutorial Window.leaveGroup\r\n */\r\n leaveGroup() {\r\n return this.wire.sendAction('leave-window-group', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Maximizes the window\r\n * @return {Promise.}\r\n * @tutorial Window.maximize\r\n */\r\n maximize() {\r\n return this.wire.sendAction('maximize-window', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Merges the instance's window group with the same window group as the specified window\r\n * @param { _Window } target The window whose group is to be merged with\r\n * @return {Promise.}\r\n * @tutorial Window.mergeGroups\r\n */\r\n mergeGroups(target) {\r\n return this.wire.sendAction('merge-window-groups', Object.assign({}, this.identity, {\r\n groupingUuid: target.identity.uuid,\r\n groupingWindowName: target.identity.name\r\n })).then(() => undefined);\r\n }\r\n /**\r\n * Minimizes the window.\r\n * @return {Promise.}\r\n * @tutorial Window.minimize\r\n */\r\n minimize() {\r\n return this.wire.sendAction('minimize-window', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Moves the window by a specified amount.\r\n * @param { number } deltaLeft The change in the left position of the window\r\n * @param { number } deltaTop The change in the top position of the window\r\n * @return {Promise.}\r\n * @tutorial Window.moveBy\r\n */\r\n moveBy(deltaLeft, deltaTop) {\r\n return this.wire.sendAction('move-window-by', Object.assign({}, this.identity, { deltaLeft, deltaTop })).then(() => undefined);\r\n }\r\n /**\r\n * Moves the window to a specified location.\r\n * @param { number } left The left position of the window\r\n * @param { number } top The top position of the window\r\n * @return {Promise.}\r\n * @tutorial Window.moveTo\r\n */\r\n moveTo(left, top) {\r\n return this.wire.sendAction('move-window', Object.assign({}, this.identity, { left, top })).then(() => undefined);\r\n }\r\n /**\r\n * Resizes the window by a specified amount.\r\n * @param { number } deltaWidth The change in the width of the window\r\n * @param { number } deltaHeight The change in the height of the window\r\n * @param { AnchorType } anchor Specifies a corner to remain fixed during the resize.\r\n * Can take the values: \"top-left\", \"top-right\", \"bottom-left\", or \"bottom-right\".\r\n * If undefined, the default is \"top-left\"\r\n * @return {Promise.}\r\n * @tutorial Window.resizeBy\r\n */\r\n resizeBy(deltaWidth, deltaHeight, anchor) {\r\n return this.wire.sendAction('resize-window-by', Object.assign({}, this.identity, {\r\n deltaWidth: Math.floor(deltaWidth),\r\n deltaHeight: Math.floor(deltaHeight),\r\n anchor\r\n })).then(() => undefined);\r\n }\r\n /**\r\n * Resizes the window to the specified dimensions.\r\n * @param { number } width The change in the width of the window\r\n * @param { number } height The change in the height of the window\r\n * @param { AnchorType } anchor Specifies a corner to remain fixed during the resize.\r\n * Can take the values: \"top-left\", \"top-right\", \"bottom-left\", or \"bottom-right\".\r\n * If undefined, the default is \"top-left\"\r\n * @return {Promise.}\r\n * @tutorial Window.resizeTo\r\n */\r\n resizeTo(width, height, anchor) {\r\n return this.wire.sendAction('resize-window', Object.assign({}, this.identity, {\r\n width: Math.floor(width),\r\n height: Math.floor(height),\r\n anchor\r\n })).then(() => undefined);\r\n }\r\n /**\r\n * Restores the window to its normal state (i.e., unminimized, unmaximized).\r\n * @return {Promise.}\r\n * @tutorial Window.restore\r\n */\r\n restore() {\r\n return this.wire.sendAction('restore-window', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Will bring the window to the front of the entire stack and give it focus.\r\n * @return {Promise.}\r\n * @tutorial Window.setAsForeground\r\n */\r\n setAsForeground() {\r\n return this.wire.sendAction('set-foreground-window', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Sets the window's size and position.\r\n * @property { Bounds } bounds This is a * @type {string} name - name of the window.object that holds the propertys of\r\n * @return {Promise.}\r\n * @tutorial Window.setBounds\r\n */\r\n setBounds(bounds) {\r\n return this.wire.sendAction('set-window-bounds', Object.assign({}, this.identity, bounds)).then(() => undefined);\r\n }\r\n /**\r\n * Shows the window if it is hidden.\r\n * @param { boolean } [force = false] Show will be prevented from showing when force is false and\r\n * â€show-requested’ has been subscribed to for application’s main window.\r\n * @return {Promise.}\r\n * @tutorial Window.show\r\n */\r\n show(force = false) {\r\n return this.wire.sendAction('show-window', Object.assign({}, this.identity, { force })).then(() => undefined);\r\n }\r\n /**\r\n * Shows the window if it is hidden at the specified location.\r\n * If the toggle parameter is set to true, the window will\r\n * alternate between showing and hiding.\r\n * @param { number } left The left position of the window\r\n * @param { number } top The right position of the window\r\n * @param { boolean } force Show will be prevented from closing when force is false and\r\n * â€show-requested’ has been subscribed to for application’s main window\r\n * @return {Promise.}\r\n * @tutorial Window.showAt\r\n */\r\n showAt(left, top, force = false) {\r\n return this.wire.sendAction('show-at-window', Object.assign({}, this.identity, {\r\n force,\r\n left: Math.floor(left),\r\n top: Math.floor(top)\r\n })).then(() => undefined);\r\n }\r\n /**\r\n * Shows the Chromium Developer Tools\r\n * @return {Promise.}\r\n * @tutorial Window.showDeveloperTools\r\n */\r\n showDeveloperTools() {\r\n return this.wire.sendAction('show-developer-tools', this.identity).then(() => undefined);\r\n }\r\n /**\r\n * Updates the window using the passed options\r\n * @param {*} options Changes a window's options that were defined upon creation. See tutorial\r\n * @return {Promise.}\r\n * @tutorial Window.updateOptions\r\n */\r\n updateOptions(options) {\r\n return this.wire.sendAction('update-window-options', Object.assign({}, this.identity, { options })).then(() => undefined);\r\n }\r\n /**\r\n * Provides credentials to authentication requests\r\n * @param { string } userName userName to provide to the authentication challange\r\n * @param { string } password password to provide to the authentication challange\r\n * @return {Promise.}\r\n * @tutorial Window.authenticate\r\n */\r\n authenticate(userName, password) {\r\n return this.wire.sendAction('window-authenticate', Object.assign({}, this.identity, { userName, password })).then(() => undefined);\r\n }\r\n /**\r\n * Returns the zoom level of the window.\r\n * @return {Promise.}\r\n * @tutorial Window.getZoomLevel\r\n */\r\n getZoomLevel() {\r\n return this.wire.sendAction('get-zoom-level', this.identity).then(({ payload }) => payload.data);\r\n }\r\n /**\r\n * Sets the zoom level of the window.\r\n * @param { number } level The zoom level\r\n * @return {Promise.}\r\n * @tutorial Window.setZoomLevel\r\n */\r\n setZoomLevel(level) {\r\n return this.wire.sendAction('set-zoom-level', Object.assign({}, this.identity, { level })).then(() => undefined);\r\n }\r\n /**\r\n * Navigates the window to a specified URL.\r\n * @param {string} url - The URL to navigate the window to.\r\n * @return {Promise.}\r\n * @tutorial Window.navigate\r\n */\r\n navigate(url) {\r\n return this.wire.sendAction('navigate-window', Object.assign({}, this.identity, { url })).then(() => undefined);\r\n }\r\n /**\r\n * Navigates the window back one page.\r\n * @return {Promise.}\r\n * @tutorial Window.navigateBack\r\n */\r\n navigateBack() {\r\n return this.wire.sendAction('navigate-window-back', Object.assign({}, this.identity)).then(() => undefined);\r\n }\r\n /**\r\n * Navigates the window forward one page.\r\n * @return {Promise.}\r\n * @tutorial Window.navigateForward\r\n */\r\n async navigateForward() {\r\n await this.wire.sendAction('navigate-window-forward', Object.assign({}, this.identity));\r\n }\r\n /**\r\n * Stops any current navigation the window is performing.\r\n * @return {Promise.}\r\n * @tutorial Window.stopNavigation\r\n */\r\n stopNavigation() {\r\n return this.wire.sendAction('stop-window-navigation', Object.assign({}, this.identity)).then(() => undefined);\r\n }\r\n}\r\nexports._Window = _Window;\r\n\n\n//# sourceURL=webpack:///./src/api/window/window.ts?"); /***/ }), /***/ "./src/environment/openfin-env.ts": /*!****************************************!*\ !*** ./src/environment/openfin-env.ts ***! \****************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst transport_errors_1 = __webpack_require__(/*! ../transport/transport-errors */ \"./src/transport/transport-errors.ts\");\r\nclass OpenFinEnvironment {\r\n constructor() {\r\n this.writeToken = (path, token) => {\r\n throw new transport_errors_1.NotImplementedError('Not Implemented');\r\n };\r\n this.retrievePort = (config) => {\r\n throw new transport_errors_1.NotImplementedError('Not Implemented');\r\n };\r\n this.getNextMessageId = () => {\r\n return fin.desktop.getUuid();\r\n };\r\n this.createChildWindow = (options) => {\r\n return new Promise((resolve, reject) => {\r\n const { uuid: parentUuid } = fin.__internal_.initialOptions;\r\n const opt = JSON.parse(JSON.stringify(options));\r\n const ABOUT_BLANK = 'about:blank';\r\n if (!name || typeof name !== 'string') {\r\n return reject(new Error('Window must have a name'));\r\n }\r\n opt.uuid = opt.uuid || parentUuid;\r\n opt.url = opt.url || ABOUT_BLANK;\r\n if (opt.uuid !== parentUuid) {\r\n return reject(new Error('Child window uuid must match the parent window\\'s uuid: ' + parentUuid));\r\n }\r\n if (this.isWindowExists(opt.uuid, opt.name)) {\r\n return reject(new Error('Trying to create a window that already exists'));\r\n }\r\n // we should register the window name with the core asap to prevent\r\n // multiple windows claiming the same uuid-name combo\r\n fin.__internal_.registerWindowName(opt.uuid, opt.name);\r\n if (opt.url !== ABOUT_BLANK) {\r\n opt.url = this.resolveUrl(opt.url);\r\n }\r\n fin.__internal_.createChildWindow(opt, (childWin) => {\r\n resolve(childWin);\r\n });\r\n });\r\n };\r\n this.getRandomId = () => {\r\n const intArray = new Uint32Array(1);\r\n return window.crypto.getRandomValues(intArray)[0].toString(32);\r\n };\r\n this.isWindowExists = (uuid, name) => {\r\n return fin.__internal_.windowExists(uuid, name);\r\n };\r\n }\r\n resolveUrl(url) {\r\n const newUrl = new URL(url, location.href);\r\n return newUrl.href;\r\n }\r\n}\r\nexports.default = OpenFinEnvironment;\r\n\n\n//# sourceURL=webpack:///./src/environment/openfin-env.ts?"); /***/ }), /***/ "./src/environment/openfin-renderer-api.ts": /*!*************************************************!*\ !*** ./src/environment/openfin-renderer-api.ts ***! \*************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\n//TODO: IPC gets deleted by the javascript adapter at the moment.\r\nexports.ipc = fin.__internal_.ipc;\r\nexports.routingId = fin.__internal_.routingId;\r\nexports.CORE_MESSAGE_CHANNEL = fin.__internal_.ipcconfig.channels.CORE_MESSAGE;\r\nexports.outboundTopic = 'of-window-message';\r\nexports.inboundTopic = `${exports.CORE_MESSAGE_CHANNEL}-${exports.routingId}`;\r\nexports.currentWindowIdentity = fin.__internal_.getWindowIdentity();\r\n\n\n//# sourceURL=webpack:///./src/environment/openfin-renderer-api.ts?"); /***/ }), /***/ "./src/of-main.ts": /*!************************!*\ !*** ./src/of-main.ts ***! \************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst fin_1 = __webpack_require__(/*! ./api/fin */ \"./src/api/fin.ts\");\r\nconst openfin_renderer_api_1 = __webpack_require__(/*! ./environment/openfin-renderer-api */ \"./src/environment/openfin-renderer-api.ts\");\r\nconst transport_1 = __webpack_require__(/*! ./transport/transport */ \"./src/transport/transport.ts\");\r\nconst elipc_1 = __webpack_require__(/*! ./transport/elipc */ \"./src/transport/elipc.ts\");\r\nconst openfin_env_1 = __webpack_require__(/*! ./environment/openfin-env */ \"./src/environment/openfin-env.ts\");\r\nconst environment = new openfin_env_1.default();\r\nconst transport = new transport_1.default(elipc_1.default, environment);\r\ntransport.connectSync(Object.assign({}, openfin_renderer_api_1.currentWindowIdentity));\r\nwindow.fin = Object.assign(window.fin, new fin_1.default(transport));\r\n\n\n//# sourceURL=webpack:///./src/of-main.ts?"); /***/ }), /***/ "./src/transport/elipc.ts": /*!********************************!*\ !*** ./src/transport/elipc.ts ***! \********************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst events_1 = __webpack_require__(/*! events */ \"./node_modules/events/events.js\");\r\nconst wire_1 = __webpack_require__(/*! ./wire */ \"./src/transport/wire.ts\");\r\nconst transport_errors_1 = __webpack_require__(/*! ./transport-errors */ \"./src/transport/transport-errors.ts\");\r\nconst openfin_renderer_api_1 = __webpack_require__(/*! ../environment/openfin-renderer-api */ \"./src/environment/openfin-renderer-api.ts\");\r\nclass ElIPCTransport extends events_1.EventEmitter {\r\n constructor(onmessage) {\r\n super();\r\n this.wire = openfin_renderer_api_1.ipc;\r\n this.connectSync = () => {\r\n openfin_renderer_api_1.ipc.on(openfin_renderer_api_1.inboundTopic, (sender, data) => {\r\n try {\r\n this.onmessage(JSON.parse(data));\r\n }\r\n catch (err) {\r\n //Do something of value here.\r\n throw err;\r\n }\r\n });\r\n };\r\n this.connect = (address) => {\r\n throw new transport_errors_1.NotImplementedError('Not Implemented');\r\n };\r\n this.onmessage = onmessage;\r\n }\r\n send(data, flags) {\r\n openfin_renderer_api_1.ipc.send(openfin_renderer_api_1.routingId, openfin_renderer_api_1.outboundTopic, data);\r\n return Promise.resolve();\r\n }\r\n shutdown() {\r\n return Promise.reject('Not Implemented');\r\n }\r\n}\r\nElIPCTransport.READY_STATE = wire_1.READY_STATE;\r\nexports.default = ElIPCTransport;\r\n\n\n//# sourceURL=webpack:///./src/transport/elipc.ts?"); /***/ }), /***/ "./src/transport/transport-errors.ts": /*!*******************************************!*\ !*** ./src/transport/transport-errors.ts ***! \*******************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst wire_1 = __webpack_require__(/*! ./wire */ \"./src/transport/wire.ts\");\r\nclass DisconnectedError extends Error {\r\n constructor(readyState) {\r\n super('Expected websocket state OPEN but found ' + wire_1.READY_STATE[readyState]);\r\n this.readyState = readyState;\r\n }\r\n}\r\nexports.DisconnectedError = DisconnectedError;\r\nclass UnexpectedActionError extends Error {\r\n}\r\nexports.UnexpectedActionError = UnexpectedActionError;\r\nclass DuplicateCorrelationError extends Error {\r\n}\r\nexports.DuplicateCorrelationError = DuplicateCorrelationError;\r\nclass NoAckError extends Error {\r\n}\r\nexports.NoAckError = NoAckError;\r\nclass NotImplementedError extends Error {\r\n}\r\nexports.NotImplementedError = NotImplementedError;\r\nclass NotSupportedError extends Error {\r\n}\r\nexports.NotSupportedError = NotSupportedError;\r\nclass RuntimeError extends Error {\r\n constructor(data) {\r\n const payload = data.payload || data;\r\n const { reason, error } = payload;\r\n super(reason);\r\n this.name = 'RuntimeError';\r\n if (error && error.stack) {\r\n this.stack = error.stack;\r\n }\r\n }\r\n}\r\nexports.RuntimeError = RuntimeError;\r\n\n\n//# sourceURL=webpack:///./src/transport/transport-errors.ts?"); /***/ }), /***/ "./src/transport/transport.ts": /*!************************************!*\ !*** ./src/transport/transport.ts ***! \************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nconst wire_1 = __webpack_require__(/*! ./wire */ \"./src/transport/wire.ts\");\r\nconst events_1 = __webpack_require__(/*! events */ \"./node_modules/events/events.js\");\r\nconst transport_errors_1 = __webpack_require__(/*! ./transport-errors */ \"./src/transport/transport-errors.ts\");\r\nconst eventAggregator_1 = __webpack_require__(/*! ../api/events/eventAggregator */ \"./src/api/events/eventAggregator.ts\");\r\nclass Transport extends events_1.EventEmitter {\r\n constructor(wireType, environment) {\r\n super();\r\n this.wireListeners = new Map();\r\n this.topicRefMap = new Map();\r\n this.eventAggregator = new eventAggregator_1.EventAggregator();\r\n this.messageHandlers = [this.eventAggregator.dispatchEvent];\r\n this.connectSync = (config) => {\r\n const { uuid, name } = config;\r\n this.me = { uuid, name };\r\n this.wire.connectSync();\r\n };\r\n /* `READY_STATE` is an instance var set by `constructor` to reference the `WebTransportSocket.READY_STATE` enum.\r\n * This is syntactic sugar that makes the enum accessible through the `wire` property of the various `fin` singletons.\r\n * For example, `fin.system.wire.READY_STATE` is a shortcut to `fin.system.wire.wire.constructor.READY_STATE`.\r\n * However it is accessed, the enum is useful for interrogating the state of the web socket on send failure.\r\n * The `err.readyState` value is passed to the `reject` handler of the promise returned by either of\r\n * `sendAction` or `ferryAction`, and hence all the API methods in the various `fin` singletons that call them.\r\n * The enum can be used in two distinct ways by the `reject` handler (using `fin.System.getVersion` by way of example):\r\n * 1. State name by state value:\r\n * fin.system.getVersion().catch(err => { console.log('State:', fin.system.wire.READY_STATE[err.readyState]); });\r\n * 2. State value by state name:\r\n * fin.system.getVersion().catch(err => { console.log('Closed:', err.readyState === fin.system.wire.READY_STATE.CLOSED); });\r\n * Note that `reject` is called when and only when `readyState` is not `OPEN`.\r\n */\r\n this.READY_STATE = wire_1.READY_STATE;\r\n this.wire = new wireType(this.onmessage.bind(this));\r\n this.environment = environment;\r\n this.sendRaw = this.wire.send.bind(this.wire);\r\n this.registerMessageHandler(this.handleMessage.bind(this));\r\n this.wire.on('disconnected', () => {\r\n for (const [, { reject }] of this.wireListeners) {\r\n reject('Remote connection has closed');\r\n }\r\n this.wireListeners.clear();\r\n this.emit('disconnected');\r\n });\r\n }\r\n async connect(config) {\r\n if (wire_1.isExistingConnectConfig(config)) {\r\n return this.connectByPort(config);\r\n }\r\n else if (wire_1.isNewConnectConfig(config)) {\r\n const port = await this.environment.retrievePort(config);\r\n return this.connectByPort(Object.assign({}, config, { address: `ws://localhost:${port}` }));\r\n }\r\n }\r\n async connectByPort(config) {\r\n const { address, uuid, name } = config;\r\n const reqAuthPayload = Object.assign({}, config, { type: 'file-token' });\r\n this.me = { uuid, name };\r\n await this.wire.connect(address);\r\n const requestExtAuthRet = await this.sendAction('request-external-authorization', {\r\n uuid,\r\n type: 'file-token'\r\n }, true);\r\n if (requestExtAuthRet.action !== 'external-authorization-response') {\r\n throw new transport_errors_1.UnexpectedActionError(requestExtAuthRet.action);\r\n }\r\n const token = requestExtAuthRet.payload.token;\r\n await this.environment.writeToken(requestExtAuthRet.payload.file, requestExtAuthRet.payload.token);\r\n const requestAuthRet = await this.sendAction('request-authorization', reqAuthPayload, true);\r\n if (requestAuthRet.action !== 'authorization-response') {\r\n throw new transport_errors_1.UnexpectedActionError(requestAuthRet.action);\r\n }\r\n else if (requestAuthRet.payload.success !== true) {\r\n throw new transport_errors_1.RuntimeError(requestAuthRet.payload);\r\n }\r\n else {\r\n return token;\r\n }\r\n }\r\n sendAction(action, payload = {}, uncorrelated = false) {\r\n return new Promise((resolve, reject) => {\r\n const id = this.environment.getNextMessageId();\r\n const msg = {\r\n action,\r\n payload,\r\n messageId: id\r\n };\r\n return this.wire.send(msg)\r\n .then(() => this.addWireListener(id, resolve, reject, uncorrelated))\r\n .catch(reject);\r\n });\r\n }\r\n ferryAction(data) {\r\n return new Promise((resolve, reject) => {\r\n const id = this.environment.getNextMessageId();\r\n data.messageId = id;\r\n const resolver = (data) => { resolve(data.payload); };\r\n return this.wire.send(data)\r\n .then(() => this.addWireListener(id, resolver, reject, false))\r\n .catch(reject);\r\n });\r\n }\r\n registerMessageHandler(handler) {\r\n this.messageHandlers.push(handler);\r\n }\r\n addWireListener(id, resolve, reject, uncorrelated) {\r\n if (uncorrelated) {\r\n this.uncorrelatedListener = resolve;\r\n }\r\n else if (this.wireListeners.has(id)) {\r\n reject(new transport_errors_1.DuplicateCorrelationError(String(id)));\r\n }\r\n else {\r\n this.wireListeners.set(id, { resolve, reject });\r\n }\r\n // Timeout and reject()?\r\n }\r\n // This method executes message handlers until the _one_ that handles the message (returns truthy) has run\r\n onmessage(data) {\r\n for (const h of this.messageHandlers) {\r\n h.call(null, data);\r\n }\r\n }\r\n handleMessage(data) {\r\n // tslint:disable-next-line\r\n const id = data.correlationId || NaN;\r\n if (!('correlationId' in data)) {\r\n if (this.uncorrelatedListener) {\r\n this.uncorrelatedListener.call(null, data);\r\n }\r\n // tslint:disable-next-line\r\n this.uncorrelatedListener = () => { };\r\n }\r\n else if (!this.wireListeners.has(id)) {\r\n return false;\r\n }\r\n else {\r\n const { resolve, reject } = this.wireListeners.get(id);\r\n if (data.action !== 'ack') {\r\n reject(new transport_errors_1.NoAckError(data.action));\r\n }\r\n else if (!('payload' in data)) {\r\n reject(new transport_errors_1.RuntimeError(data));\r\n }\r\n else if (!data.payload.success) {\r\n reject(new transport_errors_1.RuntimeError(data.payload));\r\n }\r\n else {\r\n resolve.call(null, data);\r\n }\r\n this.wireListeners.delete(id);\r\n }\r\n return true;\r\n }\r\n}\r\nexports.default = Transport;\r\nclass Message {\r\n}\r\nexports.Message = Message;\r\nclass EventMessage {\r\n}\r\nexports.EventMessage = EventMessage;\r\nclass NotificationEventMessage {\r\n}\r\nexports.NotificationEventMessage = NotificationEventMessage;\r\nclass Payload {\r\n}\r\nexports.Payload = Payload;\r\nclass AuthorizationPayload {\r\n}\r\nexports.AuthorizationPayload = AuthorizationPayload;\r\n\n\n//# sourceURL=webpack:///./src/transport/transport.ts?"); /***/ }), /***/ "./src/transport/wire.ts": /*!*******************************!*\ !*** ./src/transport/wire.ts ***! \*******************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nfunction isExternalConfig(config) {\r\n if (typeof config.manifestUrl === 'string') {\r\n return true;\r\n }\r\n}\r\nexports.isExternalConfig = isExternalConfig;\r\nfunction isExistingConnectConfig(config) {\r\n return hasUuid(config) && typeof config.address === 'string';\r\n}\r\nexports.isExistingConnectConfig = isExistingConnectConfig;\r\nfunction hasUuid(config) {\r\n return typeof config.uuid === 'string';\r\n}\r\nfunction hasRuntimeVersion(config) {\r\n return config.runtime && typeof config.runtime.version === 'string';\r\n}\r\nfunction isNewConnectConfig(config) {\r\n return hasUuid(config) && hasRuntimeVersion(config);\r\n}\r\nexports.isNewConnectConfig = isNewConnectConfig;\r\nfunction isPortDiscoveryConfig(config) {\r\n return (isExternalConfig(config) && hasRuntimeVersion(config)) || isNewConnectConfig(config);\r\n}\r\nexports.isPortDiscoveryConfig = isPortDiscoveryConfig;\r\nfunction isInternalConnectConfig(config) {\r\n return isExistingConnectConfig(config) || isNewConnectConfig(config);\r\n}\r\nexports.isInternalConnectConfig = isInternalConnectConfig;\r\nvar READY_STATE;\r\n(function (READY_STATE) {\r\n READY_STATE[READY_STATE[\"CONNECTING\"] = 0] = \"CONNECTING\";\r\n READY_STATE[READY_STATE[\"OPEN\"] = 1] = \"OPEN\";\r\n READY_STATE[READY_STATE[\"CLOSING\"] = 2] = \"CLOSING\";\r\n READY_STATE[READY_STATE[\"CLOSED\"] = 3] = \"CLOSED\"; // The connection is closed.\r\n})(READY_STATE = exports.READY_STATE || (exports.READY_STATE = {}));\r\n\n\n//# sourceURL=webpack:///./src/transport/wire.ts?"); /***/ }), /***/ "./src/util/promises.ts": /*!******************************!*\ !*** ./src/util/promises.ts ***! \******************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nfunction promisify(func) {\r\n return (...args) => new Promise((resolve, reject) => {\r\n func(...args, (err, val) => err ? reject(err) : resolve(val));\r\n });\r\n}\r\nexports.promisify = promisify;\r\nasync function promiseMap(arr, asyncF) {\r\n return Promise.all(arr.map(asyncF));\r\n}\r\nexports.promiseMap = promiseMap;\r\nasync function serial(arr) {\r\n const ret = [];\r\n for (const func of arr) {\r\n const next = await func();\r\n ret.push(next);\r\n }\r\n return ret;\r\n}\r\nexports.serial = serial;\r\nasync function promiseMapSerial(arr, func) {\r\n return serial(arr.map((value, index, array) => () => func(value, index, array)));\r\n}\r\nexports.promiseMapSerial = promiseMapSerial;\r\n\n\n//# sourceURL=webpack:///./src/util/promises.ts?"); /***/ }), /***/ "./src/util/ref-counter.ts": /*!*********************************!*\ !*** ./src/util/ref-counter.ts ***! \*********************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { "use strict"; eval("\r\nObject.defineProperty(exports, \"__esModule\", { value: true });\r\nclass RefCoutner {\r\n constructor() {\r\n this.topicRefMap = new Map();\r\n }\r\n // returns the ref count after incrementing\r\n incRefCount(key) {\r\n const refCount = this.topicRefMap.get(key);\r\n let returnCount;\r\n if (!refCount) {\r\n this.topicRefMap.set(key, 1);\r\n returnCount = 1;\r\n }\r\n else {\r\n const newRefCount = refCount + 1;\r\n returnCount = newRefCount;\r\n this.topicRefMap.set(key, newRefCount);\r\n }\r\n return returnCount;\r\n }\r\n // returns the ref count after decrementing, or -1 if the key already had no references\r\n decRefCount(key) {\r\n const refCount = this.topicRefMap.get(key);\r\n let returnCount;\r\n if (refCount) {\r\n const newRefCount = refCount - 1;\r\n this.topicRefMap.set(key, newRefCount);\r\n returnCount = newRefCount;\r\n }\r\n else {\r\n returnCount = -1;\r\n }\r\n return returnCount;\r\n }\r\n // Execute firstAction if it is the first such ref, else execute nonFirstAction.\r\n // In either case the return value is that of the action executed\r\n // tslint:disable-next-line\r\n actOnFirst(key, firstAction, nonFirstAction = () => { }) {\r\n const numRefs = this.incRefCount(key);\r\n const isFirstRef = numRefs === 1;\r\n return isFirstRef ? firstAction() : nonFirstAction();\r\n }\r\n // Execute lastAction if it is the first such ref, else execute nonLastAction.\r\n // In either case the return value is that of the action executed\r\n // tslint:disable-next-line\r\n actOnLast(key, lastAction, nonLastAction = () => { }) {\r\n const numRefs = this.decRefCount(key);\r\n const isLastRef = numRefs === 0;\r\n return isLastRef ? lastAction() : nonLastAction();\r\n }\r\n}\r\nexports.default = RefCoutner;\r\n\n\n//# sourceURL=webpack:///./src/util/ref-counter.ts?"); /***/ }) /******/ });"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const base_1 = require("../base"); const window_1 = require("../window/window"); class NavigationRejectedReply extends base_1.Reply { } exports.NavigationRejectedReply = NavigationRejectedReply; class ApplicationModule extends base_1.Base { wrap(identity) { return Promise.resolve(new Application(this.wire, identity)); } wrapSync(identity) { return new Application(this.wire, identity); } async _create(appOptions) { await this.wire.sendAction('create-application', appOptions); return await this.wrap({ uuid: appOptions.uuid }); } create(appOptions) { console.warn('Deprecation Warning: fin.Application.create is deprecated. Please use fin.Application.start'); return this._create(appOptions); } async start(appOptions) { const app = await this._create(appOptions); await this.wire.sendAction('run-application', { uuid: appOptions.uuid }); return app; } getCurrent() { return this.wrap({ uuid: this.wire.me.uuid }); } getCurrentSync() { return this.wrapSync({ uuid: this.wire.me.uuid }); } async startFromManifest(manifestUrl) { const app = await this._createFromManifest(manifestUrl); await app._run(); return app; } createFromManifest(manifestUrl) { console.warn('Deprecation Warning: fin.Application.createFromManifest is deprecated. Please use fin.Application.startFromManifest'); return this._createFromManifest(manifestUrl); } _createFromManifest(manifestUrl) { return this.wire.sendAction('get-application-manifest', { manifestUrl }) .then(({ payload }) => this.wrap({ uuid: payload.data.startup_app.uuid }) .then(app => { app._manifestUrl = manifestUrl; return app; })); } } exports.default = ApplicationModule; class Application extends base_1.EmitterBase { constructor(wire, identity) { super(wire, ['application', identity.uuid]); this.identity = identity; this.window = new window_1._Window(this.wire, { uuid: this.identity.uuid, name: this.identity.uuid }); } windowListFromIdentityList(identityList) { const windowList = []; identityList.forEach(identity => { windowList.push(new window_1._Window(this.wire, { uuid: identity.uuid, name: identity.name })); }); return windowList; } isRunning() { return this.wire.sendAction('is-application-running', this.identity) .then(({ payload }) => payload.data); } async quit(force = false) { await this._close(force); await this.wire.sendAction('destroy-application', Object.assign({ force }, this.identity)); } _close(force = false) { return this.wire.sendAction('close-application', Object.assign({}, this.identity, { force })).then(() => undefined); } close(force = false) { console.warn('Deprecation Warning: Application.close is deprecated Please use Application.quit'); return this._close(force); } getChildWindows() { return this.wire.sendAction('get-child-windows', this.identity) .then(({ payload }) => { const identityList = []; payload.data.forEach((winName) => { identityList.push({ uuid: this.identity.uuid, name: winName }); }); return this.windowListFromIdentityList(identityList); }); } getGroups() { const winGroups = []; return this.wire.sendAction('get-application-groups', Object.assign({}, this.identity, { crossApp: true })).then(({ payload }) => { payload.data.forEach((windowList, index) => { const identityList = []; windowList.forEach(winInfo => { identityList.push({ uuid: winInfo.uuid, name: winInfo.windowName }); }); winGroups[index] = this.windowListFromIdentityList(identityList); }); return winGroups; }); } getManifest() { return this.wire.sendAction('get-application-manifest', this.identity) .then(({ payload }) => payload.data); } getParentUuid() { return this.wire.sendAction('get-parent-application', this.identity) .then(({ payload }) => payload.data); } getShortcuts() { return this.wire.sendAction('get-shortcuts', this.identity) .then(({ payload }) => payload.data); } getZoomLevel() { return this.wire.sendAction('get-application-zoom-level', this.identity).then(({ payload }) => payload.data); } getWindow() { return Promise.resolve(this.window); } registerUser(userName, appName) { return this.wire.sendAction('register-user', Object.assign({}, this.identity, { userName, appName })).then(() => undefined); } removeTrayIcon() { return this.wire.sendAction('remove-tray-icon', this.identity).then(() => undefined); } restart() { return this.wire.sendAction('restart-application', this.identity).then(() => undefined); } run() { console.warn('Deprecation Warning: Application.run is deprecated Please use fin.Application.start'); return this._run(); } _run() { return this.wire.sendAction('run-application', Object.assign({}, this.identity, { manifestUrl: this._manifestUrl })).then(() => undefined); } scheduleRestart() { return this.wire.sendAction('relaunch-on-close', this.identity).then(() => undefined); } async sendApplicationLog() { const { payload } = await this.wire.sendAction('send-application-log', this.identity); return payload.data; } setTrayIcon(iconUrl) { return this.wire.sendAction('set-tray-icon', Object.assign({}, this.identity, { enabledIcon: iconUrl })).then(() => undefined); } setShortcuts(config) { return this.wire.sendAction('set-shortcuts', Object.assign({}, this.identity, { data: config })).then(() => undefined); } setZoomLevel(level) { return this.wire.sendAction('set-application-zoom-level', Object.assign({}, this.identity, { level })).then(() => undefined); } async setAppLogUsername(username) { await this.wire.sendAction('set-app-log-username', Object.assign({ data: username }, this.identity)); } getTrayIconInfo() { return this.wire.sendAction('get-tray-icon-info', this.identity) .then(({ payload }) => payload.data); } terminate() { return this.wire.sendAction('terminate-application', this.identity).then(() => undefined); } wait() { return this.wire.sendAction('wait-for-hung-application', this.identity).then(() => undefined); } getInfo() { return this.wire.sendAction('get-info', this.identity).then(({ payload }) => payload.data); } } exports.Application = Application; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const promises_1 = require("../util/promises"); class Base { constructor(wire) { this.isNodeEnvironment = () => { return this.wire.environment.constructor.name === 'NodeEnvironment'; }; this.isOpenFinEnvironment = () => { return this.wire.environment.constructor.name === 'OpenFinEnvironment'; }; this.wire = wire; } get topic() { return this._topic || this.constructor.name.replace('_', '').toLowerCase(); } set topic(t) { this._topic = t; } get me() { return this.wire.me; } } exports.Base = Base; class EmitterBase extends Base { constructor(wire, emitterAccessor) { super(wire); this.emitterAccessor = emitterAccessor; this.eventNames = () => this.hasEmitter() ? this.getEmitter().eventNames() : []; this.emit = (eventName, payload, ...args) => { return this.hasEmitter() ? this.getEmitter().emit(eventName, payload, ...args) : false; }; this.hasEmitter = () => this.wire.eventAggregator.has(this.emitterAccessor); this.getEmitter = () => this.wire.eventAggregator.get(this.emitterAccessor); this.listeners = (type) => this.hasEmitter() ? this.getEmitter().listeners(type) : []; this.listenerCount = (type) => this.hasEmitter() ? this.getEmitter().listenerCount(type) : 0; this.registerEventListener = async (eventType, options = {}) => { const runtimeEvent = Object.assign({}, this.identity, { timestamp: options.timestamp || Date.now(), topic: this.topic, type: eventType }); const emitter = this.getEmitter(); const refCount = emitter.listenerCount(runtimeEvent.type); if (!refCount) { await this.wire.sendAction('subscribe-to-desktop-event', runtimeEvent); } return emitter; }; this.deregisterEventListener = async (eventType, options = {}) => { if (this.hasEmitter()) { const runtimeEvent = Object.assign({}, this.identity, { timestamp: options.timestamp || Date.now(), topic: this.topic, type: eventType }); const emitter = this.getEmitter(); const refCount = emitter.listenerCount(runtimeEvent.type); const newRefCount = refCount - 1; if (newRefCount === 0) { await this.wire.sendAction('unsubscribe-to-desktop-event', runtimeEvent); if (emitter.eventNames && emitter.eventNames().length === 0) { this.wire.eventAggregator.delete(this.emitterAccessor); return; } } return emitter; } return Promise.resolve(); }; this.on = async (eventType, listener, options) => { const emitter = await this.registerEventListener(eventType, options); emitter.on(eventType, listener); return this; }; this.addListener = this.on; this.once = async (eventType, listener, options) => { const deregister = () => this.deregisterEventListener(eventType); const emitter = await this.registerEventListener(eventType, options); emitter.once(eventType, deregister); emitter.once(eventType, listener); return this; }; this.prependListener = async (eventType, listener, options) => { const emitter = await this.registerEventListener(eventType, options); emitter.prependListener(eventType, listener); return this; }; this.prependOnceListener = async (eventType, listener, options) => { const deregister = () => this.deregisterEventListener(eventType); const emitter = await this.registerEventListener(eventType, options); emitter.prependOnceListener(eventType, listener); emitter.once(eventType, deregister); return this; }; this.removeListener = async (eventType, listener, options) => { const emitter = await this.deregisterEventListener(eventType, options); if (emitter) { emitter.removeListener(eventType, listener); } return this; }; this.deregisterAllListeners = async (eventType) => { const runtimeEvent = Object.assign({}, this.identity, { type: eventType, topic: this.topic }); if (this.hasEmitter()) { await this.wire.sendAction('unsubscribe-to-desktop-event', runtimeEvent); const emitter = this.getEmitter(); emitter.removeAllListeners(eventType); if (emitter.eventNames().length === 0) { this.wire.eventAggregator.delete(this.emitterAccessor); return; } return emitter; } }; this.removeAllListeners = async (eventType) => { const removeByEvent = async (event) => { const emitter = await this.deregisterAllListeners(event); if (emitter) { emitter.removeAllListeners(event); } }; if (eventType) { await removeByEvent(eventType); } else if (this.hasEmitter()) { const events = this.getEmitter().eventNames(); await promises_1.promiseMap(events, removeByEvent); } return this; }; this.listeners = (event) => this.hasEmitter() ? this.getEmitter().listeners(event) : []; } } exports.EmitterBase = EmitterBase; class Reply { } exports.Reply = Reply; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const base_1 = require("../base"); class Clipboard extends base_1.Base { writeText(writeObj) { return this.wire.sendAction('clipboard-write-text', writeObj).then(() => undefined); } readText(type) { return this.wire.sendAction('clipboard-read-text', type) .then(({ payload }) => payload.data); } writeHtml(writeObj) { return this.wire.sendAction('clipboard-write-html', writeObj).then(() => undefined); } readHtml(type) { return this.wire.sendAction('clipboard-read-html', type) .then(({ payload }) => payload.data); } writeRtf(writeObj) { return this.wire.sendAction('clipboard-write-rtf', writeObj).then(() => undefined); } readRtf(type) { return this.wire.sendAction('clipboard-read-rtf', type) .then(({ payload }) => payload.data); } write(writeObj) { return this.wire.sendAction('clipboard-write', writeObj).then(() => undefined); } getAvailableFormats(type) { return this.wire.sendAction('clipboard-read-formats', type) .then(({ payload }) => payload.data); } } exports.default = Clipboard; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function getTopic(e) { switch (e.topic) { case 'window': return 'window'; } } exports.getTopic = getTopic; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); class EmitterMap { constructor() { this.storage = new Map(); } hashKeys(keys) { const hashed = keys.map(normalizeString); return hashed.join('/'); } get(keys) { const hash = this.hashKeys(keys); if (!this.storage.has(hash)) { this.storage.set(hash, new events_1.EventEmitter()); } return this.storage.get(hash); } has(keys) { return this.storage.has(this.hashKeys(keys)); } delete(keys) { const hash = this.hashKeys(keys); return this.storage.delete(hash); } } exports.EmitterMap = EmitterMap; function normalizeString(s) { const b = new Buffer(s); return b.toString('base64'); } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const emitterMap_1 = require("./emitterMap"); function isEventMessage(message) { return message.action === 'process-desktop-event'; } function isNotificationMessage(message) { return message.action === 'process-notification-event'; } const buildLocalPayload = (rawPayload) => { const { payload: { message }, type } = rawPayload; const payload = {}; switch (type) { case 'message': payload.message = message; break; case 'show': case 'error': case 'click': case 'close': default: break; } return payload; }; function mapKeyFromEvent(event) { const { topic } = event; if (topic === 'frame') { const { uuid, name } = event; return [topic, uuid, name]; } if (topic === 'window') { const { uuid, name } = event; return [topic, uuid, name]; } if (topic === 'application') { const { uuid } = event; return [topic, uuid]; } return [topic]; } class EventAggregator extends emitterMap_1.EmitterMap { constructor() { super(...arguments); this.dispatchEvent = (message) => { if (isEventMessage(message)) { const { payload } = message; const accessor = mapKeyFromEvent(payload); if (this.has(accessor)) { this.get(accessor).emit(payload.type, payload); return true; } } else if (isNotificationMessage(message)) { const { payload: { notificationId }, type } = message.payload; const accessor = ['notification', '' + notificationId]; if (this.has(accessor)) { this.get(accessor).emit(type, buildLocalPayload(message.payload)); return true; } } return false; }; } } exports.EventAggregator = EventAggregator; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const base_1 = require("../base"); class ExternalApplicationModule extends base_1.Base { wrap(uuid) { return Promise.resolve(new ExternalApplication(this.wire, { uuid })); } wrapSync(uuid) { return new ExternalApplication(this.wire, { uuid }); } } exports.default = ExternalApplicationModule; class ExternalApplication extends base_1.EmitterBase { constructor(wire, identity) { super(wire, ['external-application', identity.uuid]); this.identity = identity; } getInfo() { return this.wire.sendAction('get-external-application-info', this.identity).then(({ payload }) => payload.data); } } exports.ExternalApplication = ExternalApplication; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const system_1 = require("./system/system"); const window_1 = require("./window/window"); const application_1 = require("./application/application"); const interappbus_1 = require("./interappbus/interappbus"); const notification_1 = require("./notification/notification"); const clipboard_1 = require("./clipboard/clipboard"); const external_application_1 = require("./external-application/external-application"); const frame_1 = require("./frame/frame"); const global_hotkey_1 = require("./global-hotkey"); class Fin extends events_1.EventEmitter { get me() { return this.wire.me; } constructor(wire) { super(); this.wire = wire; this.System = new system_1.default(wire); this.Window = new window_1.default(wire); this.Application = new application_1.default(wire); this.InterApplicationBus = new interappbus_1.default(wire); this.Notification = new notification_1.default(wire); this.Clipboard = new clipboard_1.default(wire); this.ExternalApplication = new external_application_1.default(wire); this.Frame = new frame_1.default(wire); this.GlobalHotkey = new global_hotkey_1.default(wire); wire.on('disconnected', () => { this.emit('disconnected'); }); } } exports.default = Fin; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const base_1 = require("../base"); class _FrameModule extends base_1.Base { wrap(identity) { return Promise.resolve(new _Frame(this.wire, identity)); } wrapSync(identity) { return new _Frame(this.wire, identity); } getCurrent() { return Promise.resolve(new _Frame(this.wire, this.me)); } getCurrentSync() { return new _Frame(this.wire, this.me); } } exports.default = _FrameModule; class _Frame extends base_1.EmitterBase { constructor(wire, identity) { super(wire, ['frame', identity.uuid, identity.name]); this.identity = identity; } getInfo() { return this.wire.sendAction('get-frame-info', this.identity).then(({ payload }) => payload.data); } getParentWindow() { return this.wire.sendAction('get-parent-window', this.identity).then(({ payload }) => payload.data); } } exports._Frame = _Frame; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const base_1 = require("../base"); var apiActions; (function (apiActions) { apiActions["REGISTER"] = "global-hotkey-register"; apiActions["UNREGISTER"] = "global-hotkey-unregister"; apiActions["UNREGISTER_ALL"] = "global-hotkey-unregister-all"; apiActions["IS_REGISTERED"] = "global-hotkey-is-registered"; })(apiActions || (apiActions = {})); var nonHotkeyEvents; (function (nonHotkeyEvents) { nonHotkeyEvents["REGISTERED"] = "registered"; nonHotkeyEvents["UNREGISTERED"] = "unregistered"; })(nonHotkeyEvents = exports.nonHotkeyEvents || (exports.nonHotkeyEvents = {})); class GlobalHotkey extends base_1.EmitterBase { constructor(wire) { super(wire, ['global-hotkey']); this.topic = 'global-hotkey'; } async register(hotkey, listener) { await this.on(hotkey, listener); await this.wire.sendAction("global-hotkey-register", { hotkey }); return void 0; } async unregister(hotkey) { await this.removeAllListeners(hotkey); await this.wire.sendAction("global-hotkey-unregister", { hotkey }); return void 0; } async unregisterAll() { await Promise.all(this.eventNames() .filter((name) => !(name === "registered" || name === "unregistered")) .map((name) => this.removeAllListeners(name))); await this.wire.sendAction("global-hotkey-unregister-all", {}); return void 0; } async isRegistered(hotkey) { const { payload: { data } } = await this.wire.sendAction("global-hotkey-is-registered", { hotkey }); return data; } } exports.default = GlobalHotkey; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const idOrResult = (func) => (...args) => { const res = func(...args); return res === undefined ? args[1] : res; }; class ChannelBase { constructor(providerIdentity, send) { this.defaultSet = false; this.providerIdentity = providerIdentity; this.subscriptions = new Map(); this.defaultAction = () => { throw new Error('No action registered'); }; this.sendRaw = send; this.send = async (to, action, payload) => { const raw = await send('send-channel-message', Object.assign({}, to, { providerIdentity: this.providerIdentity, action, payload })) .catch(reason => { throw new Error(reason.message); }); return raw.payload.data.result; }; } async processAction(action, payload, senderIdentity) { try { const mainAction = this.subscriptions.has(action) ? this.subscriptions.get(action) : (payload, id) => this.defaultAction(action, payload, id); const preActionProcessed = this.preAction ? await this.preAction(action, payload, senderIdentity) : payload; const actionProcessed = await mainAction(preActionProcessed, senderIdentity); return this.postAction ? await this.postAction(action, actionProcessed, senderIdentity) : actionProcessed; } catch (e) { if (this.errorMiddleware) { return this.errorMiddleware(action, e, senderIdentity); } throw e; } } beforeAction(func) { if (this.preAction) { throw new Error('Already registered beforeAction middleware'); } this.preAction = idOrResult(func); } onError(func) { if (this.errorMiddleware) { throw new Error('Already registered error middleware'); } this.errorMiddleware = func; } afterAction(func) { if (this.postAction) { throw new Error('Already registered afterAction middleware'); } this.postAction = idOrResult(func); } remove(action) { this.subscriptions.delete(action); } setDefaultAction(func) { if (this.defaultSet) { throw new Error('default action can only be set once'); } else { this.defaultAction = func; this.defaultSet = true; } } register(topic, listener) { if (this.subscriptions.has(topic)) { throw new Error(`Subscription already registered for action: ${topic}. Unsubscribe before adding new subscription`); } else { this.subscriptions.set(topic, listener); return true; } } } exports.ChannelBase = ChannelBase; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const channel_1 = require("./channel"); class ChannelClient extends channel_1.ChannelBase { constructor(providerIdentity, send) { super(providerIdentity, send); this.disconnectListener = () => undefined; } async dispatch(action, payload) { return this.send(this.providerIdentity, action, payload); } onDisconnection(listener) { this.disconnectListener = listener; } async disconnect() { const { channelName } = this.providerIdentity; await this.sendRaw('disconnect-from-channel', { channelName }); const { channelId } = this.providerIdentity; this.removeChannel(channelId); } } exports.ChannelClient = ChannelClient; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const client_1 = require("./client"); const provider_1 = require("./provider"); const base_1 = require("../../base"); class Channel extends base_1.EmitterBase { constructor(wire) { super(wire, ['channel']); this.onmessage = (msg) => { if (msg.action === 'process-channel-message') { this.processChannelMessage(msg); return true; } else if (msg.action === 'process-channel-connection') { this.processChannelConnection(msg); return true; } return false; }; this.topic = 'channel'; this.channelMap = new Map(); wire.registerMessageHandler(this.onmessage.bind(this)); } async getAllChannels() { return this.wire.sendAction('get-all-channels') .then(({ payload }) => payload.data); } async onChannelConnect(listener) { await this.on('connected', listener); } async onChannelDisconnect(listener) { await this.on('disconnected', listener); } async connect(channelName, options) { if (!channelName || typeof channelName !== 'string') { throw new Error('Please provide a channelName string to connect to a channel.'); } const opts = options || {}; let resolver; let listener; const waitResponse = new Promise(resolve => { resolver = resolve; listener = (payload) => { if (channelName === payload.channelName) { this.removeListener('connected', listener); this.connect(channelName, opts).then(response => { resolve(response); }); } }; this.on('connected', listener); }); try { const { payload: { data: providerIdentity } } = await this.wire.sendAction('connect-to-channel', Object.assign({ channelName }, opts)); if (resolver) { resolver(); } this.removeListener('connected', listener); const channel = new client_1.ChannelClient(providerIdentity, this.wire.sendAction.bind(this.wire)); const key = providerIdentity.channelId; this.channelMap.set(key, channel); channel.removeChannel = this.removeChannelFromMap.bind(this); this.on('disconnected', (eventPayload) => { if (eventPayload.channelName === channelName) { this.removeChannelFromMap(key); channel.disconnectListener(eventPayload); } }); return channel; } catch (e) { const shouldWait = Object.assign({ wait: true }, opts).wait; const internalNackMessage = 'internal-nack'; if (shouldWait && e.message && e.message.includes(internalNackMessage)) { console.warn(`Channel not found for channelName: ${channelName}, waiting for channel creation.`); return await waitResponse; } else if (e.message === internalNackMessage) { throw new Error(`No channel found for channelName: ${channelName}`); } else { throw new Error(e); } } } async create(channelName) { if (!channelName) { throw new Error('Please provide a channelName to create a channel'); } const { payload: { data: providerIdentity } } = await this.wire.sendAction('create-channel', { channelName }); const channel = new provider_1.ChannelProvider(providerIdentity, this.wire.sendAction.bind(this.wire)); const key = providerIdentity.channelId; this.channelMap.set(key, channel); channel.removeChannel = this.removeChannelFromMap.bind(this); this.on('client-disconnected', (eventPayload) => { if (eventPayload.channelName === channelName) { channel.connections = channel.connections.filter(identity => { return identity.uuid !== eventPayload.uuid || identity.name !== eventPayload.name; }); channel.disconnectListener(eventPayload); } }); return channel; } removeChannelFromMap(mapKey) { this.channelMap.delete(mapKey); } async processChannelMessage(msg) { const { senderIdentity, providerIdentity, action, ackToSender, payload } = msg.payload; const key = providerIdentity.channelId; const bus = this.channelMap.get(key); if (!bus) { ackToSender.payload.success = false; ackToSender.payload.reason = `Client connection with identity ${JSON.stringify(this.wire.me)} no longer connected.`; return this.wire.sendRaw(ackToSender); } try { const res = await bus.processAction(action, payload, senderIdentity); ackToSender.payload.payload = ackToSender.payload.payload || {}; ackToSender.payload.payload.result = res; this.wire.sendRaw(ackToSender); } catch (e) { ackToSender.payload.success = false; ackToSender.payload.reason = e.message; this.wire.sendRaw(ackToSender); } } async processChannelConnection(msg) { const { clientIdentity, providerIdentity, ackToSender, payload } = msg.payload; const key = providerIdentity.channelId; const bus = this.channelMap.get(key); if (!bus) { ackToSender.payload.success = false; ackToSender.payload.reason = `Channel "${providerIdentity.channelName}" has been destroyed.`; return this.wire.sendRaw(ackToSender); } try { if (!(bus instanceof provider_1.ChannelProvider)) { throw Error('Cannot connect to a channel client'); } const res = await bus.processConnection(clientIdentity, payload); ackToSender.payload.payload = ackToSender.payload.payload || {}; ackToSender.payload.payload.result = res; this.wire.sendRaw(ackToSender); } catch (e) { ackToSender.payload.success = false; ackToSender.payload.reason = e.message; this.wire.sendRaw(ackToSender); } } } exports.Channel = Channel; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const channel_1 = require("./channel"); class ChannelProvider extends channel_1.ChannelBase { constructor(providerIdentity, send) { super(providerIdentity, send); this.connectListener = () => undefined; this.disconnectListener = () => undefined; this.connections = []; } dispatch(to, action, payload) { return this.send(to, action, payload); } async processConnection(senderId, payload) { this.connections.push(senderId); return this.connectListener(senderId, payload); } publish(action, payload) { return this.connections.map(to => this.send(to, action, payload)); } onConnection(listener) { this.connectListener = listener; } onDisconnection(listener) { this.disconnectListener = listener; } async destroy() { const { channelName } = this.providerIdentity; await this.sendRaw('destroy-channel', { channelName }); const { channelId } = this.providerIdentity; this.removeChannel(channelId); } } exports.ChannelProvider = ChannelProvider; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const base_1 = require("../base"); const ref_counter_1 = require("../../util/ref-counter"); const events_1 = require("events"); const index_1 = require("./channel/index"); class InterApplicationBus extends base_1.Base { constructor(wire) { super(wire); this.events = { subscriberAdded: 'subscriber-added', subscriberRemoved: 'subscriber-removed' }; this.refCounter = new ref_counter_1.default(); this.Channel = new index_1.Channel(wire); this.emitter = new events_1.EventEmitter(); wire.registerMessageHandler(this.onmessage.bind(this)); this.on = this.emitter.on.bind(this.emitter); this.removeAllListeners = this.emitter.removeAllListeners.bind(this.emitter); } publish(topic, message) { return this.wire.sendAction('publish-message', { topic, message, sourceWindowName: this.me.name }).then(() => undefined); } send(destination, topic, message) { return this.wire.sendAction('send-message', { destinationUuid: destination.uuid, destinationWindowName: destination.name, topic, message, sourceWindowName: this.me.name }).then(() => undefined); } subscribe(source, topic, listener) { const subKey = this.createSubscriptionKey(source.uuid, source.name || '*', topic); const sendSubscription = () => { return this.wire.sendAction('subscribe', { sourceUuid: source.uuid, sourceWindowName: source.name || '*', topic, destinationWindowName: this.me.name }); }; const alreadySubscribed = () => { return new Promise(r => r).then(() => undefined); }; this.emitter.on(subKey, listener); return this.refCounter.actOnFirst(subKey, sendSubscription, alreadySubscribed); } unsubscribe(source, topic, listener) { const subKey = this.createSubscriptionKey(source.uuid, source.name || '*', topic); const sendUnsubscription = () => { return this.wire.sendAction('unsubscribe', { sourceUuid: source.uuid, sourceWindowName: source.name || '*', topic, destinationWindowName: this.me.name }); }; const dontSendUnsubscription = () => { return new Promise(r => r).then(() => undefined); }; this.emitter.removeListener(subKey, listener); return this.refCounter.actOnLast(subKey, sendUnsubscription, dontSendUnsubscription); } processMessage(message) { const { payload: { message: payloadMessage, sourceWindowName, sourceUuid, topic } } = message; const keys = [ this.createSubscriptionKey(sourceUuid, sourceWindowName, topic), this.createSubscriptionKey(sourceUuid, '*', topic), this.createSubscriptionKey('*', '*', topic) ]; const idOfSender = { uuid: sourceUuid, name: sourceWindowName }; keys.forEach((key) => { this.emitter.emit(key, payloadMessage, idOfSender); }); } emitSubscriverEvent(type, message) { const { payload: { name, uuid, topic } } = message; const payload = { name, uuid, topic }; this.emitter.emit(type, payload); } createSubscriptionKey(uuid, name, topic) { const n = name || '*'; if (!(uuid && n && topic)) { throw new Error('Missing uuid, name, or topic string'); } return createKey(uuid, n, topic); } onmessage(message) { const { action } = message; switch (action) { case 'process-message': this.processMessage(message); break; case this.events.subscriberAdded: this.emitSubscriverEvent(this.events.subscriberAdded, message); break; case this.events.subscriberRemoved: this.emitSubscriverEvent(this.events.subscriberRemoved, message); break; default: break; } return true; } } exports.default = InterApplicationBus; class InterAppPayload { } exports.InterAppPayload = InterAppPayload; function createKey(...toHash) { return toHash.map((item) => { return (new Buffer('' + item)).toString('base64'); }).join('/'); } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const base_1 = require("../base"); const events = { show: 'show', close: 'close', error: 'error', click: 'click', message: 'message' }; class NotificationOptions { constructor(options = {}, identity, notificationId) { const { url, message, timeout, ignoreMouseOver } = options; this.url = url; this.message = message || null; this.timeout = timeout; this.notificationId = notificationId; this.uuidOfProxiedApp = identity.uuid; this.ignoreMouseOver = ignoreMouseOver; } } exports.NotificationOptions = NotificationOptions; class _Notification extends base_1.EmitterBase { constructor(wire, options) { super(wire, ['notification', '' + options.notificationId]); this.listenerList = ['newListener']; this.unhookAllListeners = () => { this.listenerList.forEach(event => { this.removeAllListeners(event); }); this.listenerList.length = 0; }; this.options = options; this.url = options.url; this.timeout = options.timeout; this.message = options.message; this.notificationId = options.notificationId; this.on('newListener', (event) => { this.listenerList.push(event); }); this.on('close', () => { setTimeout(this.unhookAllListeners, 1); }); } async show() { if (!this.url) { throw new Error('Notifications require a url'); } await this.wire.sendAction('send-action-to-notifications-center', { action: 'create-notification', payload: { url: this.url, notificationId: this.options.notificationId, message: { message: this.message }, timeout: this.timeout } }); } async sendMessage(message) { await this.wire.sendAction('send-action-to-notifications-center', { action: 'send-notification-message', payload: { notificationId: this.options.notificationId, message: { message } } }); } async close() { await this.wire.sendAction('send-action-to-notifications-center', { action: 'close-notification', payload: { notificationId: this.options.notificationId } }); } } exports._Notification = _Notification; class _NotificationModule extends base_1.Base { constructor() { super(...arguments); this.nextNoteId = 0; this.events = events; } genNoteId() { return ++this.nextNoteId; } ; create(options) { const noteOptions = new NotificationOptions(options, this.me, this.genNoteId()); return new _Notification(this.wire, noteOptions); } ; } exports.default = _NotificationModule; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const base_1 = require("../base"); const transport_errors_1 = require("../../transport/transport-errors"); const window_1 = require("../window/window"); class System extends base_1.EmitterBase { constructor(wire) { super(wire, ['system']); } sendExternalProcessRequest(action, options) { return new Promise((resolve, reject) => { const exitEventKey = 'external-process-exited'; let processUuid; let externalProcessExitHandler; let ofWindow; if (typeof options.listener === 'function') { externalProcessExitHandler = (payload) => { const data = payload || {}; const exitPayload = { topic: 'exited', uuid: data.processUuid || '', exitCode: data.exitCode || 0 }; if (processUuid === payload.processUuid) { options.listener(exitPayload); ofWindow.removeListener(exitEventKey, externalProcessExitHandler); } }; if (!this.wire.me.name) { this.wire.me.name = this.wire.me.uuid; } ofWindow = new window_1._Window(this.wire, this.wire.me); ofWindow.on(exitEventKey, externalProcessExitHandler); } this.wire.sendAction(action, options) .then(({ payload }) => { processUuid = payload.data.uuid; resolve(payload.data); }).catch((err) => { if (ofWindow) { ofWindow.removeListener(exitEventKey, externalProcessExitHandler); } reject(err); }); }); } getVersion() { return this.wire.sendAction('get-version') .then(({ payload }) => payload.data); } clearCache(options) { return this.wire.sendAction('clear-cache', options).then(() => undefined); } deleteCacheOnExit() { return this.wire.sendAction('delete-cache-request').then(() => undefined); } exit() { return this.wire.sendAction('exit-desktop').then(() => undefined); } flushCookieStore() { return this.wire.sendAction('flush-cookie-store').then(() => undefined); } getAllWindows() { return this.wire.sendAction('get-all-windows') .then(({ payload }) => payload.data); } getAllApplications() { return this.wire.sendAction('get-all-applications') .then(({ payload }) => payload.data); } getCommandLineArguments() { return this.wire.sendAction('get-command-line-arguments') .then(({ payload }) => payload.data); } getCrashReporterState() { return this.wire.sendAction('get-crash-reporter-state').then(({ payload }) => payload.data); } getDeviceId() { console.warn('Function is deprecated; use getMachineId instead.'); return this.wire.sendAction('get-device-id').then(({ payload }) => payload.data); } startCrashReporter(options) { return new Promise((resolve, reject) => { if (!options.diagnosticMode) { return reject(new Error('diagnosticMode not found in options')); } this.wire.sendAction('start-crash-reporter', options).then(({ payload }) => resolve(payload.data)).catch(err => reject(err)); }); } getDeviceUserId() { return this.wire.sendAction('get-device-user-id').then(({ payload }) => payload.data); } getEntityInfo(uuid, name) { return this.wire.sendAction('get-entity-info', { uuid, name }).then(({ payload }) => payload.data); } getEnvironmentVariable(envName) { return this.wire.sendAction('get-environment-variable', { environmentVariables: envName }) .then(({ payload }) => payload.data); } getFocusedWindow() { return this.wire.sendAction('get-focused-window').then(({ payload }) => payload.data); } getLog(options) { return this.wire.sendAction('view-log', options) .then(({ payload }) => payload.data); } getMachineId() { return this.wire.sendAction('get-machine-id').then(({ payload }) => payload.data); } getMinLogLevel() { return this.wire.sendAction('get-min-log-level').then(({ payload }) => payload.data); } getLogList() { return this.wire.sendAction('list-logs') .then(({ payload }) => payload.data); } getMonitorInfo() { return this.wire.sendAction('get-monitor-info') .then(({ payload }) => payload.data); } getMousePosition() { return this.wire.sendAction('get-mouse-position') .then(({ payload }) => payload.data); } getProcessList() { return this.wire.sendAction('process-snapshot') .then(({ payload }) => payload.data); } getProxySettings() { return this.wire.sendAction('get-proxy-settings') .then(({ payload }) => payload.data); } getRuntimeInfo() { return this.wire.sendAction('get-runtime-info').then(({ payload }) => payload.data); } getRvmInfo() { return this.wire.sendAction('get-rvm-info') .then(({ payload }) => payload.data); } getHostSpecs() { return this.wire.sendAction('get-host-specs').then(({ payload }) => payload.data); } launchExternalProcess(options) { return this.sendExternalProcessRequest('launch-external-process', options); } monitorExternalProcess(options) { return this.sendExternalProcessRequest('monitor-external-process', options); } log(level, message) { return this.wire.sendAction('write-to-log', { level, message }).then(() => undefined); } openUrlWithBrowser(url) { return this.wire.sendAction('open-url-with-browser', { url }).then(() => undefined); } releaseExternalProcess(uuid) { return this.wire.sendAction('release-external-process', { uuid }).then(() => undefined); } showDeveloperTools(identity) { return this.wire.sendAction('show-developer-tools', identity).then(() => undefined); } terminateExternalProcess(options) { return this.wire.sendAction('terminate-external-process', options) .then(() => undefined); } updateProxySettings(options) { return this.wire.sendAction('update-proxy', options).then(() => undefined); } downloadAsset(appAsset, progressListener) { return new Promise((resolve, reject) => { if (this.wire.environment.constructor.name === 'NodeEnvironment') { reject(new transport_errors_1.NotSupportedError('downloadAsset only supported in an OpenFin Render process')); return; } const downloadId = this.wire.environment.getNextMessageId().toString(); const dlProgressKey = `asset-download-progress-${downloadId}`; const dlErrorKey = `asset-download-error-${downloadId}`; const dlCompleteKey = `asset-download-complete-${downloadId}`; const dlProgress = (progress) => { const p = { downloadedBytes: progress.downloadedBytes, totalBytes: progress.totalBytes }; progressListener(p); }; const cleanListeners = () => { this.removeListener(dlProgressKey, dlProgress); }; const dlError = (r, err) => { const error = err ? err : r; cleanListeners(); reject(new transport_errors_1.RuntimeError(error)); }; const dlComplete = () => { cleanListeners(); resolve(); }; this.on(dlProgressKey, dlProgress); this.once(dlErrorKey, dlError); this.once(dlCompleteKey, dlComplete); const downloadOptions = Object.assign(appAsset, { downloadId }); this.wire.sendAction('download-asset', downloadOptions).catch((err) => { cleanListeners(); reject(err); }); }); } downloadRuntime(options, progressListener) { return new Promise((resolve, reject) => { if (this.wire.environment.constructor.name === 'NodeEnvironment') { reject(new transport_errors_1.NotSupportedError('downloadRuntime only supported in an OpenFin Render process')); return; } const downloadId = this.wire.environment.getNextMessageId().toString(); const dlProgressKey = `runtime-download-progress-${downloadId}`; const dlErrorKey = `runtime-download-error-${downloadId}`; const dlCompleteKey = `runtime-download-complete-${downloadId}`; const dlProgress = (progress) => { const p = { downloadedBytes: progress.downloadedBytes, totalBytes: progress.totalBytes }; progressListener(p); }; const cleanListeners = () => { this.removeListener(dlProgressKey, dlProgress); }; const dlError = (r, err) => { const error = err ? err : r; cleanListeners(); reject(new transport_errors_1.RuntimeError(error)); }; const dlComplete = () => { cleanListeners(); resolve(); }; this.on(dlProgressKey, dlProgress); this.once(dlErrorKey, dlError); this.once(dlCompleteKey, dlComplete); const downloadOptions = Object.assign(options, { downloadId }); this.wire.sendAction('download-runtime', downloadOptions).catch((err) => { cleanListeners(); reject(err); }); }); } downloadPreloadScripts(scripts) { return this.wire.sendAction('download-preload-scripts', { scripts }).then(({ payload }) => payload.data); } getAllExternalApplications() { return this.wire.sendAction('get-all-external-applications') .then(({ payload }) => payload.data); } getAppAssetInfo(options) { return this.wire.sendAction('get-app-asset-info', options).then(({ payload }) => payload.data); } getCookies(options) { return this.wire.sendAction('get-cookies', options).then(({ payload }) => payload.data); } setMinLogLevel(level) { return this.wire.sendAction('set-min-log-level', { level }).then(() => undefined); } resolveUuid(uuid) { return this.wire.sendAction('resolve-uuid', { entityKey: uuid }).then(({ payload }) => payload.data); } executeOnRemote(requestingIdentity, data) { data.requestingIdentity = requestingIdentity; return this.wire.ferryAction(data); } readRegistryValue(rootKey, subkey, value) { return this.wire.sendAction('read-registry-value', { rootKey: rootKey, subkey: subkey, value: value }).then(({ payload }) => payload.data); } registerExternalConnection(uuid) { return this.wire.sendAction('register-external-connection', { uuid }).then(({ payload }) => payload.data); } } exports.default = System; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const base_1 = require("../base"); const application_1 = require("../application/application"); class _WindowModule extends base_1.Base { async wrap(identity) { return new _Window(this.wire, identity); } wrapSync(identity) { return new _Window(this.wire, identity); } create(options) { const win = new _Window(this.wire, { uuid: this.me.uuid, name: options.name }); return win.createWindow(options); } getCurrent() { return this.wrap(this.wire.me); } getCurrentSync() { return this.wrapSync(this.wire.me); } } exports.default = _WindowModule; class _Window extends base_1.EmitterBase { constructor(wire, identity) { super(wire, ['window', identity.uuid, identity.name]); this.identity = identity; } createWindow(options) { return new Promise((resolve, reject) => { const CONSTRUCTOR_CB_TOPIC = 'fire-constructor-callback'; const pageResponse = new Promise((resolve) => { this.on(CONSTRUCTOR_CB_TOPIC, function fireConstructor(response) { let cbPayload; const success = response.success; const responseData = response.data; const message = responseData.message; if (success) { cbPayload = { httpResponseCode: responseData.httpResponseCode, apiInjected: responseData.apiInjected }; } else { cbPayload = { message: responseData.message, networkErrorCode: responseData.networkErrorCode, stack: responseData.stack }; } this.removeListener(CONSTRUCTOR_CB_TOPIC, fireConstructor); resolve({ message: message, cbPayload: cbPayload, success: success }); }); }); const windowCreation = this.wire.environment.createChildWindow(options); Promise.all([pageResponse, windowCreation]).then((resolvedArr) => { const pageResolve = resolvedArr[0]; if (pageResolve.success) { resolve(this); } else { reject(pageResolve); } }); }); } windowListFromNameList(identityList) { const windowList = []; identityList.forEach(identity => { windowList.push(new _Window(this.wire, { uuid: identity.uuid, name: identity.name })); }); return windowList; } getAllFrames() { return this.wire.sendAction('get-all-frames', this.identity).then(({ payload }) => payload.data); } getBounds() { return this.wire.sendAction('get-window-bounds', this.identity) .then(({ payload }) => payload.data); } focus() { return this.wire.sendAction('focus-window', this.identity).then(() => undefined); } blur() { return this.wire.sendAction('blur-window', this.identity).then(() => undefined); } bringToFront() { return this.wire.sendAction('bring-window-to-front', this.identity).then(() => undefined); } animate(transitions, options) { return this.wire.sendAction('animate-window', Object.assign({}, this.identity, { transitions, options })).then(() => undefined); } hide() { return this.wire.sendAction('hide-window', this.identity).then(() => undefined); } close(force = false) { return this.wire.sendAction('close-window', Object.assign({}, this.identity, { force })) .then(() => { Object.setPrototypeOf(this, null); return undefined; }); } getNativeId() { return this.wire.sendAction('get-window-native-id', this.identity) .then(({ payload }) => payload.data); } disableFrame() { console.warn('Function is deprecated; use disableUserMovement instead.'); return this.wire.sendAction('disable-window-frame', this.identity).then(() => undefined); } disableUserMovement() { return this.wire.sendAction('disable-window-frame', this.identity).then(() => undefined); } enableFrame() { console.warn('Function is deprecated; use enableUserMovement instead.'); return this.wire.sendAction('enable-window-frame', this.identity).then(() => undefined); } enableUserMovement() { return this.wire.sendAction('enable-window-frame', this.identity).then(() => undefined); } executeJavaScript(code) { return this.wire.sendAction('execute-javascript-in-window', Object.assign({}, this.identity, { code })) .then(() => undefined); } flash() { return this.wire.sendAction('flash-window', this.identity).then(() => undefined); } stopFlashing() { return this.wire.sendAction('stop-flash-window', this.identity).then(() => undefined); } getGroup() { return this.wire.sendAction('get-window-group', Object.assign({}, this.identity, { crossApp: true })).then(({ payload }) => { let winGroup = []; if (payload.data.length) { winGroup = this.windowListFromNameList(payload.data); } return winGroup; }); } getInfo() { return this.wire.sendAction('get-window-info', this.identity).then(({ payload }) => payload.data); } getOptions() { return this.wire.sendAction('get-window-options', this.identity).then(({ payload }) => payload.data); } getParentApplication() { return Promise.resolve(new application_1.Application(this.wire, this.identity)); } getParentWindow() { return Promise.resolve(new application_1.Application(this.wire, this.identity)).then(app => app.getWindow()); } async getSnapshot(area) { const req = Object.assign({}, this.identity, { area }); const res = await this.wire.sendAction('get-window-snapshot', req); return res.payload.data; } getState() { return this.wire.sendAction('get-window-state', this.identity).then(({ payload }) => payload.data); } isMainWindow() { return this.me.uuid === this.me.name; } isShowing() { return this.wire.sendAction('is-window-showing', this.identity).then(({ payload }) => payload.data); } joinGroup(target) { return this.wire.sendAction('join-window-group', Object.assign({}, this.identity, { groupingUuid: target.identity.uuid, groupingWindowName: target.identity.name })).then(() => undefined); } reload(ignoreCache = false) { return this.wire.sendAction('reload-window', Object.assign({}, this.identity, { ignoreCache })).then(() => undefined); } leaveGroup() { return this.wire.sendAction('leave-window-group', this.identity).then(() => undefined); } maximize() { return this.wire.sendAction('maximize-window', this.identity).then(() => undefined); } mergeGroups(target) { return this.wire.sendAction('merge-window-groups', Object.assign({}, this.identity, { groupingUuid: target.identity.uuid, groupingWindowName: target.identity.name })).then(() => undefined); } minimize() { return this.wire.sendAction('minimize-window', this.identity).then(() => undefined); } moveBy(deltaLeft, deltaTop) { return this.wire.sendAction('move-window-by', Object.assign({}, this.identity, { deltaLeft, deltaTop })).then(() => undefined); } moveTo(left, top) { return this.wire.sendAction('move-window', Object.assign({}, this.identity, { left, top })).then(() => undefined); } resizeBy(deltaWidth, deltaHeight, anchor) { return this.wire.sendAction('resize-window-by', Object.assign({}, this.identity, { deltaWidth: Math.floor(deltaWidth), deltaHeight: Math.floor(deltaHeight), anchor })).then(() => undefined); } resizeTo(width, height, anchor) { return this.wire.sendAction('resize-window', Object.assign({}, this.identity, { width: Math.floor(width), height: Math.floor(height), anchor })).then(() => undefined); } restore() { return this.wire.sendAction('restore-window', this.identity).then(() => undefined); } setAsForeground() { return this.wire.sendAction('set-foreground-window', this.identity).then(() => undefined); } setBounds(bounds) { return this.wire.sendAction('set-window-bounds', Object.assign({}, this.identity, bounds)).then(() => undefined); } show(force = false) { return this.wire.sendAction('show-window', Object.assign({}, this.identity, { force })).then(() => undefined); } showAt(left, top, force = false) { return this.wire.sendAction('show-at-window', Object.assign({}, this.identity, { force, left: Math.floor(left), top: Math.floor(top) })).then(() => undefined); } showDeveloperTools() { return this.wire.sendAction('show-developer-tools', this.identity).then(() => undefined); } updateOptions(options) { return this.wire.sendAction('update-window-options', Object.assign({}, this.identity, { options })).then(() => undefined); } authenticate(userName, password) { return this.wire.sendAction('window-authenticate', Object.assign({}, this.identity, { userName, password })).then(() => undefined); } getZoomLevel() { return this.wire.sendAction('get-zoom-level', this.identity).then(({ payload }) => payload.data); } setZoomLevel(level) { return this.wire.sendAction('set-zoom-level', Object.assign({}, this.identity, { level })).then(() => undefined); } navigate(url) { return this.wire.sendAction('navigate-window', Object.assign({}, this.identity, { url })).then(() => undefined); } navigateBack() { return this.wire.sendAction('navigate-window-back', Object.assign({}, this.identity)).then(() => undefined); } async navigateForward() { await this.wire.sendAction('navigate-window-forward', Object.assign({}, this.identity)); } stopNavigation() { return this.wire.sendAction('stop-window-navigation', Object.assign({}, this.identity)).then(() => undefined); } } exports._Window = _Window; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.notImplementedEnvErrorMsg = 'Not implemented in this environment'; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = require("fs"); const crypto_1 = require("crypto"); const port_discovery_1 = require("../transport/port-discovery"); const transport_errors_1 = require("../transport/transport-errors"); class NodeEnvironment { constructor() { this.messageCounter = 0; this.writeToken = (path, token) => { return new Promise(resolve => { fs_1.writeFile(path, token, () => resolve(token)); }); }; this.retrievePort = (config) => { const pd = new port_discovery_1.PortDiscovery(config, this); return pd.retrievePort(); }; this.getNextMessageId = () => { return this.messageCounter++; }; this.createChildWindow = (options) => { throw new transport_errors_1.NotImplementedError('Not Implemented'); }; this.getRandomId = () => { return crypto_1.randomBytes(16).toString('hex'); }; this.isWindowExists = (uuid, name) => { throw new transport_errors_1.NotImplementedError('Not Implemented'); }; } } exports.default = NodeEnvironment; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const os = require("os"); const path = require("path"); const win_launch_1 = require("./win-launch"); const nix_launch_1 = require("./nix-launch"); class Launcher { constructor() { this.OpenFin_Installer = 'OpenFinInstaller.exe'; this.Installer_Work_Dir = path.join(os.tmpdir(), 'openfinnode'); this.Security_Realm_Config_Key = '--security-realm='; this.os = os.platform(); if (this.os !== 'win32') { if (this.os === 'darwin') { this.nixConfig = { urlPath: 'mac/x64', executablePath: 'OpenFin.app/Contents/MacOS/OpenFin' }; } else if (this.os === 'linux') { this.nixConfig = { urlPath: `linux/${os.arch()}`, executablePath: 'openfin' }; } else { throw new Error(`Launching not supported on ${this.os}`); } } } launch(config, manifestLocation, namedPipeName) { if (this.os === 'win32') { return this.winLaunch(config, manifestLocation, namedPipeName); } else if (this.os === 'darwin') { const osConf = Object.assign({ manifestLocation, namedPipeName }, this.nixConfig); return this.macLaunch(config, osConf); } else if (this.os === 'linux') { const osConf = Object.assign({ manifestLocation, namedPipeName, urlPath: `linux/${os.arch()}`, executablePath: 'openfin' }, this.nixConfig); return this.macLaunch(config, osConf); } else { throw new Error(`Launching not supported on ${this.os}`); } } static IS_SUPPORTED() { const platform = os.platform(); return platform === 'win32' || platform === 'darwin' || os.platform() === 'linux'; } macLaunch(config, osConfig) { return nix_launch_1.default(config, osConfig); } winLaunch(config, manifestLocation, namedPipeName) { return win_launch_1.default(config, manifestLocation, namedPipeName, this.Installer_Work_Dir); } } exports.default = Launcher; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const path = require("path"); const child_process_1 = require("child_process"); const promises_1 = require("../util/promises"); const util_1 = require("./util"); const mkdir = promises_1.promisify(fs.mkdir); const downloads = {}; function getUrl(version, urlPath) { const runtimeRoot = process.env.assetsUrl || 'https://cdn.openfin.co/release/runtime/'; return `${runtimeRoot}${urlPath}/${version}`; } exports.getUrl = getUrl; async function download(version, folder, osConfig) { const url = getUrl(version, osConfig.urlPath); const tmp = 'tmp'; await util_1.rmDir(folder, false); await mkdir(path.join(folder, tmp)).catch(e => { }); const file = path.join(folder, tmp, 'tmp'); await util_1.downloadFile(url, file); await util_1.unzip(file, folder); await util_1.rmDir(path.join(folder, tmp), true); return folder; } exports.download = download; async function getRuntimePath(version) { if (process.env.runtimeDirectory) { return util_1.resolveDir(process.env.runtimeDirectory, ['Runtime', version]); } const versionPath = ['OpenFin', 'Runtime', version]; const HOME = process.env.HOME; return util_1.resolveDir(HOME, versionPath); } exports.getRuntimePath = getRuntimePath; async function install(versionOrChannel, osConfig) { const version = await util_1.resolveRuntimeVersion(versionOrChannel); const rtFolder = await getRuntimePath(version); const rtPath = path.join(rtFolder, osConfig.executablePath); const rtExists = await util_1.exists(rtPath); if (Boolean(rtExists)) { await promises_1.promisify(fs.chmod)(rtPath, 0o755); } else { try { if (!downloads[version]) { downloads[version] = download(version, rtFolder, osConfig); } await downloads[version]; downloads[version] = undefined; } catch (err) { console.error(`Failed to download, attempting to empty ${rtFolder}`); await util_1.rmDir(rtFolder, false); throw Error(`Could not install runtime ${versionOrChannel} (${version})`); } } return rtPath; } exports.install = install; async function launch(config, osConfig) { try { let fb = false; const runtimePath = await install(config.runtime.version, osConfig) .catch(e => { if (config.runtime.fallbackVersion !== undefined) { fb = true; console.warn(`could not install openfin ${config.runtime.version}`); console.warn(`trying fallback ${config.runtime.fallbackVersion}`); return install(config.runtime.fallbackVersion, osConfig); } return Promise.reject(e); }); const args = config.runtime.arguments ? config.runtime.arguments.split(' ') : []; if (process.env.runtimeArgs) { args.push(process.env.runtimeArgs.split(' ')); } args.unshift(`--startup-url=${osConfig.manifestLocation}`); args.push(`--version-keyword=${fb ? config.runtime.fallbackVersion : config.runtime.version}`); args.push(`--runtime-information-channel-v6=${osConfig.namedPipeName}`); if (config.runtime.securityRealm) { args.push(`--security-realm=${config.runtime.securityRealm}`); } if (config.runtime.verboseLogging) { args.push('--v=1'); args.push('--attach-console'); } return child_process_1.spawn(runtimePath, args); } catch (e) { console.error('Failed to launch\n', e); throw e; } } exports.default = launch; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = require("path"); const https = require("https"); const fs = require("fs"); const child_process_1 = require("child_process"); const promises_1 = require("../util/promises"); const stat = promises_1.promisify(fs.stat); async function exists(path) { try { const exists = await stat(path); return Boolean(exists); } catch (e) { return false; } } exports.exists = exists; async function get(url) { return new Promise((resolve, reject) => { const request = https.get(url, (response) => { if (response.statusCode < 200 || response.statusCode > 299) { reject(new Error('Failed to load page, status code: ' + response.statusCode)); } const body = []; response.on('data', (chunk) => { body.push(chunk); }); response.on('end', () => resolve(body.join(''))); }); request.on('error', (err) => reject(err)); }); } exports.get = get; async function unzip(file, dest) { const ex = promises_1.promisify(child_process_1.exec); return ex(`unzip ${file} -d ${dest}`, { encoding: 'utf8' }); } exports.unzip = unzip; const lstat = promises_1.promisify(fs.lstat); const unlink = promises_1.promisify(fs.unlink); const readdir = promises_1.promisify(fs.readdir); const rmdir = promises_1.promisify(fs.rmdir); async function rmDir(dirPath, removeSelf = true) { let files; try { files = await readdir(dirPath); } catch (e) { return; } if (files.length > 0) { await promises_1.promiseMap(files, async (f) => { const filePath = dirPath + '/' + f; const file = await lstat(filePath); if (file.isFile() || file.isSymbolicLink()) { await unlink(filePath); } else { await rmDir(filePath, true); } }); } if (removeSelf) { await rmdir(dirPath); } } exports.rmDir = rmDir; async function downloadFile(url, writeLocation) { return new Promise((resolve, reject) => { try { https.get(url, (response) => { if (response.statusCode !== 200) { if (response.statusCode === 404) { reject(new Error('Specified runtime not available for OS')); } else { reject(new Error('Issue Downloading ' + response.statusCode)); } } else { const file = fs.createWriteStream(writeLocation); response.pipe(file); file.on('finish', () => { file.close(); resolve(); }); } }); } catch (e) { reject(e); } }); } exports.downloadFile = downloadFile; async function resolveRuntimeVersion(versionOrChannel) { const splitVersion = versionOrChannel.split('.'); const isVersion = splitVersion.length > 1 && splitVersion.every(x => x === '*' || /^\d+$/.test(x)); if (isVersion) { const mustMatch = takeWhile(splitVersion, (x) => x !== '*'); if (4 - mustMatch.length > 0) { const res = await get('https://cdn.openfin.co/release/runtimeVersions'); const versions = res.split('\r\n'); const match = first(versions, (v) => v.split('.').slice(0, mustMatch.length).join('.') === mustMatch.join('.')); if (match) { return match; } } else { return versionOrChannel; } } try { return await get(`https://cdn.openfin.co/release/runtime/${versionOrChannel}`); } catch (err) { throw Error('Could not resolve runtime version'); } } exports.resolveRuntimeVersion = resolveRuntimeVersion; function first(arr, func) { for (let i = 0; i < arr.length; i++) { if (func(arr[i], i, arr)) { return arr[i]; } } return null; } exports.first = first; function takeWhile(arr, func) { return arr.reduce(({ take, vals }, x, i, r) => take && func(x, i, r) ? { take: true, vals: [...vals, x] } : { take: false, vals }, { take: true, vals: [] }) .vals; } const mkdir = promises_1.promisify(fs.mkdir); async function resolveDir(base, paths) { return await paths.reduce(async (p, next) => { try { const prev = await p; await mkdir(path.resolve(prev, next)); return path.join(prev, next); } catch (err) { return err.code === 'EEXIST' ? err.path : Promise.reject(err); } }, Promise.resolve(base)); } exports.resolveDir = resolveDir; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const os = require("os"); const path = require("path"); const child_process_1 = require("child_process"); const util_1 = require("./util"); const OpenFin_Installer = 'OpenFinInstaller.exe'; function copyInstaller(Installer_Work_Dir) { return new Promise(async (resolve, reject) => { await util_1.resolveDir(os.tmpdir(), ['openfinnode']); const rd = fs.createReadStream(path.join(__dirname, '..', '..', 'resources', 'win', OpenFin_Installer)); const outf = path.join(Installer_Work_Dir, OpenFin_Installer); const wr = fs.createWriteStream(outf); wr.on('error', (err) => reject(err)); wr.on('finish', () => { resolve(outf); }); rd.pipe(wr); }); } async function checkRVMAsync(config, Installer_Work_Dir, manifestLocation) { const rvmPath = path.resolve(process.env.LOCALAPPDATA, 'OpenFin', 'OpenFinRVM.exe'); if (!await util_1.exists(rvmPath)) { await new Promise(async (resolve, reject) => { const installer = await copyInstaller(Installer_Work_Dir); const installing = child_process_1.spawn(installer, [`--config=${manifestLocation}`, '--do-not-launch']); installing.on('exit', (code) => { resolve(); }); installing.on('error', reject); }); if (!await util_1.exists(rvmPath)) { throw new Error('Failed to install the RVM'); } } return rvmPath; } function launchRVM(config, manifestLocation, namedPipeName, rvm) { const runtimeArgs = `--runtime-arguments=--runtime-information-channel-v6=${namedPipeName}`; const rvmArgs = []; if (config.installerUI !== true) { rvmArgs.push('--no-ui'); } rvmArgs.push(`--config=${manifestLocation}`); rvmArgs.push(runtimeArgs); if (config.runtime.rvmDir) { rvmArgs.push(`--working-dir=${config.runtime.rvmDir}`); } if (config.assetsUrl) { rvmArgs.push(`--assetsUrl=${config.assetsUrl}`); } return child_process_1.spawn(rvm, rvmArgs, { stdio: ['pipe', 'ignore', 'pipe'] }); } const checkRVM = makeQueued(checkRVMAsync); async function launch(config, manifestLocation, namedPipeName, Installer_Work_Dir) { const rvmPath = await checkRVM(config, Installer_Work_Dir, manifestLocation); return await launchRVM(config, manifestLocation, namedPipeName, rvmPath); } exports.default = launch; function makeQueued(func) { let initial; return async function (...args) { const x = initial || Promise.resolve(); initial = x .then(() => new Promise((resolve, reject) => setImmediate(() => resolve()))) .then(() => func(...args)) .catch(() => func(...args)); return initial; }; } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fin_1 = require("./api/fin"); exports.Fin = fin_1.default; const application_1 = require("./api/application/application"); exports.Application = application_1.Application; const window_1 = require("./api/window/window"); exports.Window = window_1._Window; const frame_1 = require("./api/frame/frame"); exports.Frame = frame_1._Frame; const notification_1 = require("./api/notification/notification"); exports.Notification = notification_1._Notification; const system_1 = require("./api/system/system"); exports.System = system_1.default; const wire_1 = require("./transport/wire"); const node_env_1 = require("./environment/node-env"); const transport_1 = require("./transport/transport"); const websocket_1 = require("./transport/websocket"); const port_discovery_1 = require("./transport/port-discovery"); const normalize_config_1 = require("./util/normalize-config"); const environment = new node_env_1.default(); async function connect(config) { const wire = new transport_1.default(websocket_1.default, environment); const normalized = await normalize_config_1.validateConfig(config); await wire.connect(normalized); return new fin_1.default(wire); } exports.connect = connect; async function launch(config) { const normalized = await normalize_config_1.normalizeConfig(config); if (!wire_1.isPortDiscoveryConfig(normalized)) { throw new Error('Invalid Config'); } const pd = new port_discovery_1.PortDiscovery(normalized, environment); return pd.retrievePort(); } exports.launch = launch; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require("fs"); const net = require("net"); const path = require("path"); const os = require("os"); const wire_1 = require("./wire"); const launcher_1 = require("../launcher/launcher"); const timers_1 = require("timers"); const launcher = new launcher_1.default(); const MessageHeaderSize = 20; var ChromiumMessageType; (function (ChromiumMessageType) { ChromiumMessageType[ChromiumMessageType["RUNTIME_HELLO_MESSAGE"] = Math.pow(2, 16) - 1] = "RUNTIME_HELLO_MESSAGE"; ChromiumMessageType[ChromiumMessageType["RUNTIME_STRING_MESSAGE"] = 0] = "RUNTIME_STRING_MESSAGE"; })(ChromiumMessageType || (ChromiumMessageType = {})); var DiscoverState; (function (DiscoverState) { DiscoverState[DiscoverState["INIT"] = 0] = "INIT"; DiscoverState[DiscoverState["HELLO"] = 1] = "HELLO"; DiscoverState[DiscoverState["PORT_MESSAGE"] = 2] = "PORT_MESSAGE"; })(DiscoverState || (DiscoverState = {})); function matchRuntimeInstance(config, message) { const args = config.runtime.arguments || ''; const realm = config.runtime.securityRealm || (args.split('--security-realm=')[1] || '').split(' ')[0]; if (config.runtime.version && realm) { return config.runtime.version === message.requestedVersion && realm === message.securityRealm; } else if (config.runtime.version) { return config.runtime.version === message.requestedVersion && !message.securityRealm; } else { return false; } } function generateManifest(config) { const manifest = Object.assign({}, { devtools_port: config.devToolsPort }, { startup_app: config.startupApp }, { lrsUrl: config.lrsUrl }, { assetsUrl: config.assetsUrl }, { licenseKey: config.licenseKey }, { appAssets: config.appAssets }); if (config.runtime) { let runtimeArgs = ''; manifest.runtime = Object.assign({}, { version: config.runtime.version, fallbackVersion: config.runtime.fallbackVersion }); if (config.runtime.securityRealm) { runtimeArgs = runtimeArgs.concat(`${launcher.Security_Realm_Config_Key}${config.runtime.securityRealm} `); } if (config.runtime.verboseLogging === true) { runtimeArgs = runtimeArgs.concat('--v=1 --attach-console '); } if (config.runtime.arguments) { runtimeArgs = runtimeArgs.concat(`${config.runtime.arguments}`); } manifest.runtime.arguments = runtimeArgs; } if (config.customItems) { config.customItems.forEach((value) => { Object.assign(manifest, value); }); } return manifest; } function onRuntimeHello(data, conn) { const header = readHeader(data); if (header.message_type === ChromiumMessageType.RUNTIME_HELLO_MESSAGE) { let helloPayload = readUint32(data, MessageHeaderSize); if (helloPayload === 0) { header.extraInteger = true; helloPayload = readUint32(data, MessageHeaderSize + 4); } writeHelloMessage(header, conn); } else { console.error(`Invalid port discovery hello message type ${header.message_type}`); } } function onDiscoverMessage(data) { const header = readHeader(data); if (header.message_type === ChromiumMessageType.RUNTIME_STRING_MESSAGE) { const strLength = readUint32(data, MessageHeaderSize); let msg; if (os.platform() !== 'win32') { const raw = data.toString('utf8'); const firstBrace = raw.indexOf('{'); const lastBrace = raw.lastIndexOf('}'); msg = raw.slice(firstBrace, lastBrace + 1); } else { msg = data.toString('utf8', MessageHeaderSize + 4, MessageHeaderSize + 4 + strLength); } const msg2 = msg.replace(/\\/g, '\\\\'); const env = JSON.parse(msg2); if (env.payload) { return env.payload; } else { console.warn('discovery message did not have payload'); } } else { console.error(`Invalid port discovery message type ${header.message_type}`); } } function readHeader(data) { const header = {}; header.payload_size = readUint32(data, 0); header.routing_id = readUint32(data, 4); header.message_type = readUint32(data, 8); header.flags = readUint32(data, 12); header.attachment_count = readUint32(data, 16); return header; } function writeHelloMessage(header, conn) { const data = Buffer.alloc(MessageHeaderSize + (header.extraInteger ? 28 : 4)); writeUint32(data, header.payload_size, 0); writeUint32(data, header.routing_id, 4); writeUint32(data, header.message_type, 8); writeUint32(data, header.flags, 12); writeUint32(data, header.attachment_count, 16); let next = 20; if (header.extraInteger) { writeUint32(data, 0, next); next += 4; } writeUint32(data, process.pid, next); conn.write(data); } function readUint32(data, offset) { return data.readInt32LE(offset); } function writeUint32(data, value, offset) { data.writeInt32LE(value, offset); } class PortDiscovery { constructor(config, environment) { this.savedConfig = Object.assign({}, config); this.environment = environment; } async retrievePort() { try { await this.createManifest(); await this.createDiscoveryNamedPipe(); const mPromise = this.listenDiscoveryMessage(); const msg = await Promise.race([(async () => { const openfin = await launcher.launch(this.savedConfig, this.manifestLocation, this.namedPipeName); openfin.on('error', err => { throw err; }); if (this.savedConfig.runtime.verboseLogging) { openfin.stdout.pipe(process.stdout); openfin.stderr.pipe(process.stderr); } this.timeoutTimer = timers_1.setTimeout(() => { console.warn('Port Discovery is taking a while. Either the runtime is downloading or it failed to retrieve the port.'); }, 30 * 1000); return await mPromise; })(), mPromise]); if (matchRuntimeInstance(this.savedConfig, msg)) { this.cleanup(); return msg.port; } else { console.warn('Port Discovery did not match runtime instance'); } } catch (reason) { this.cleanup(); throw reason; } } createDiscoveryNamedPipe() { return new Promise((resolve, reject) => { let unix = false; const randomNum = this.environment.getRandomId(); this.namedPipeName = 'NodeAdapter.' + randomNum; this.namedPipeServer = net.createServer(); const pipePath = os.platform() === 'win32' ? path.join('\\\\.\\pipe\\', 'chrome.' + this.namedPipeName) : path.join(os.tmpdir(), this.namedPipeName + '.sock'); if (os.platform() !== 'win32') { unix = true; this.namedPipeName = pipePath; } this.namedPipeServer.listen(pipePath, () => { if (unix) { const address = this.namedPipeServer.address(); this.namedPipeName = address; fs.chmodSync(pipePath, 0o777); } resolve(); }); }); } listenDiscoveryMessage() { return new Promise((resolve, reject) => { this.namedPipeServer.on('connection', (conn) => { let discoverState = DiscoverState.INIT; conn.on('data', (data) => { if (discoverState === DiscoverState.INIT) { onRuntimeHello(data, conn); discoverState = DiscoverState.HELLO; } else if (discoverState === DiscoverState.HELLO) { const msg = onDiscoverMessage(data); if (msg && matchRuntimeInstance(this.savedConfig, msg)) { resolve(msg); } else { console.warn('Received Port Discovery message from unexpected runtime'); } } }); }); this.namedPipeServer.on('error', err => reject(err)); }); } createManifest() { return new Promise((resolve, reject) => { if (wire_1.isExternalConfig(this.savedConfig)) { this.manifestLocation = this.savedConfig.manifestUrl; resolve(); } else { const manifestFileName = 'NodeAdapter-' + this.savedConfig.uuid.replace(/ /g, '-') + '.json'; try { fs.mkdirSync(launcher.Installer_Work_Dir); } catch (e) { if (!e.message.includes('file already exists')) { reject(new Error(`Error creating work directory ${e.message}`)); return; } } this.manifestLocation = path.join(launcher.Installer_Work_Dir, manifestFileName); const wr = fs.createWriteStream(this.manifestLocation); const manifest = generateManifest(this.savedConfig); wr.on('error', (err) => reject(err)); wr.on('finish', () => { resolve(); }); wr.write(JSON.stringify(manifest), () => { wr.end(); }); } }); } cleanup() { if (this.namedPipeServer) { if (this.pipeConnection) { this.pipeConnection.end(); } this.namedPipeServer.close(); } if (this.timeoutTimer) { clearTimeout(this.timeoutTimer); } } } exports.PortDiscovery = PortDiscovery; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const wire_1 = require("./wire"); class DisconnectedError extends Error { constructor(readyState) { super('Expected websocket state OPEN but found ' + wire_1.READY_STATE[readyState]); this.readyState = readyState; } } exports.DisconnectedError = DisconnectedError; class UnexpectedActionError extends Error { } exports.UnexpectedActionError = UnexpectedActionError; class DuplicateCorrelationError extends Error { } exports.DuplicateCorrelationError = DuplicateCorrelationError; class NoAckError extends Error { } exports.NoAckError = NoAckError; class NotImplementedError extends Error { } exports.NotImplementedError = NotImplementedError; class NotSupportedError extends Error { } exports.NotSupportedError = NotSupportedError; class RuntimeError extends Error { constructor(data) { const payload = data.payload || data; const { reason, error } = payload; super(reason); this.name = 'RuntimeError'; if (error && error.stack) { this.stack = error.stack; } } } exports.RuntimeError = RuntimeError; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const wire_1 = require("./wire"); const events_1 = require("events"); const transport_errors_1 = require("./transport-errors"); const eventAggregator_1 = require("../api/events/eventAggregator"); class Transport extends events_1.EventEmitter { constructor(wireType, environment) { super(); this.wireListeners = new Map(); this.topicRefMap = new Map(); this.eventAggregator = new eventAggregator_1.EventAggregator(); this.messageHandlers = [this.eventAggregator.dispatchEvent]; this.connectSync = (config) => { const { uuid, name } = config; this.me = { uuid, name }; this.wire.connectSync(); }; this.READY_STATE = wire_1.READY_STATE; this.wire = new wireType(this.onmessage.bind(this)); this.environment = environment; this.sendRaw = this.wire.send.bind(this.wire); this.registerMessageHandler(this.handleMessage.bind(this)); this.wire.on('disconnected', () => { for (const [, { reject }] of this.wireListeners) { reject('Remote connection has closed'); } this.wireListeners.clear(); this.emit('disconnected'); }); } async connect(config) { if (wire_1.isExistingConnectConfig(config)) { return this.connectByPort(config); } else if (wire_1.isNewConnectConfig(config)) { const port = await this.environment.retrievePort(config); return this.connectByPort(Object.assign({}, config, { address: `ws://localhost:${port}` })); } } async connectByPort(config) { const { address, uuid, name } = config; const reqAuthPayload = Object.assign({}, config, { type: 'file-token' }); this.me = { uuid, name }; await this.wire.connect(address); const requestExtAuthRet = await this.sendAction('request-external-authorization', { uuid, type: 'file-token' }, true); if (requestExtAuthRet.action !== 'external-authorization-response') { throw new transport_errors_1.UnexpectedActionError(requestExtAuthRet.action); } const token = requestExtAuthRet.payload.token; await this.environment.writeToken(requestExtAuthRet.payload.file, requestExtAuthRet.payload.token); const requestAuthRet = await this.sendAction('request-authorization', reqAuthPayload, true); if (requestAuthRet.action !== 'authorization-response') { throw new transport_errors_1.UnexpectedActionError(requestAuthRet.action); } else if (requestAuthRet.payload.success !== true) { throw new transport_errors_1.RuntimeError(requestAuthRet.payload); } else { return token; } } sendAction(action, payload = {}, uncorrelated = false) { return new Promise((resolve, reject) => { const id = this.environment.getNextMessageId(); const msg = { action, payload, messageId: id }; return this.wire.send(msg) .then(() => this.addWireListener(id, resolve, reject, uncorrelated)) .catch(reject); }); } ferryAction(data) { return new Promise((resolve, reject) => { const id = this.environment.getNextMessageId(); data.messageId = id; const resolver = (data) => { resolve(data.payload); }; return this.wire.send(data) .then(() => this.addWireListener(id, resolver, reject, false)) .catch(reject); }); } registerMessageHandler(handler) { this.messageHandlers.push(handler); } addWireListener(id, resolve, reject, uncorrelated) { if (uncorrelated) { this.uncorrelatedListener = resolve; } else if (this.wireListeners.has(id)) { reject(new transport_errors_1.DuplicateCorrelationError(String(id))); } else { this.wireListeners.set(id, { resolve, reject }); } } onmessage(data) { for (const h of this.messageHandlers) { h.call(null, data); } } handleMessage(data) { const id = data.correlationId || NaN; if (!('correlationId' in data)) { if (this.uncorrelatedListener) { this.uncorrelatedListener.call(null, data); } this.uncorrelatedListener = () => { }; } else if (!this.wireListeners.has(id)) { return false; } else { const { resolve, reject } = this.wireListeners.get(id); if (data.action !== 'ack') { reject(new transport_errors_1.NoAckError(data.action)); } else if (!('payload' in data)) { reject(new transport_errors_1.RuntimeError(data)); } else if (!data.payload.success) { reject(new transport_errors_1.RuntimeError(data.payload)); } else { resolve.call(null, data); } this.wireListeners.delete(id); } return true; } } exports.default = Transport; class Message { } exports.Message = Message; class EventMessage { } exports.EventMessage = EventMessage; class NotificationEventMessage { } exports.NotificationEventMessage = NotificationEventMessage; class Payload { } exports.Payload = Payload; class AuthorizationPayload { } exports.AuthorizationPayload = AuthorizationPayload; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const WebSocket = require("ws"); const wire_1 = require("./wire"); const transport_errors_1 = require("./transport-errors"); class WebSocketTransport extends events_1.EventEmitter { constructor(onmessage) { super(); this.connect = (address) => { return new Promise((resolve, reject) => { this.wire = new WebSocket(address); this.wire.addEventListener('open', resolve); this.wire.addEventListener('error', reject); this.wire.addEventListener('ping', this.wire.pong); this.wire.addEventListener('message', (message, flags) => this.onmessage.call(null, JSON.parse(message.data))); this.wire.addEventListener('close', () => { this.emit('disconnected'); }); }); }; this.connectSync = () => { throw new transport_errors_1.NotImplementedError('Not Implemented'); }; this.onmessage = onmessage; } send(data, flags) { return new Promise((resolve, reject) => { if (this.wire.readyState !== wire_1.READY_STATE.OPEN) { reject(new transport_errors_1.DisconnectedError(this.wire.readyState)); } else { this.wire.send(JSON.stringify(data), flags, resolve); } }); } shutdown() { this.wire.terminate(); return Promise.resolve(); } } WebSocketTransport.READY_STATE = wire_1.READY_STATE; exports.default = WebSocketTransport; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function isExternalConfig(config) { if (typeof config.manifestUrl === 'string') { return true; } } exports.isExternalConfig = isExternalConfig; function isExistingConnectConfig(config) { return hasUuid(config) && typeof config.address === 'string'; } exports.isExistingConnectConfig = isExistingConnectConfig; function hasUuid(config) { return typeof config.uuid === 'string'; } function hasRuntimeVersion(config) { return config.runtime && typeof config.runtime.version === 'string'; } function isNewConnectConfig(config) { return hasUuid(config) && hasRuntimeVersion(config); } exports.isNewConnectConfig = isNewConnectConfig; function isPortDiscoveryConfig(config) { return (isExternalConfig(config) && hasRuntimeVersion(config)) || isNewConnectConfig(config); } exports.isPortDiscoveryConfig = isPortDiscoveryConfig; function isInternalConnectConfig(config) { return isExistingConnectConfig(config) || isNewConnectConfig(config); } exports.isInternalConnectConfig = isInternalConnectConfig; var READY_STATE; (function (READY_STATE) { READY_STATE[READY_STATE["CONNECTING"] = 0] = "CONNECTING"; READY_STATE[READY_STATE["OPEN"] = 1] = "OPEN"; READY_STATE[READY_STATE["CLOSING"] = 2] = "CLOSING"; READY_STATE[READY_STATE["CLOSED"] = 3] = "CLOSED"; })(READY_STATE = exports.READY_STATE || (exports.READY_STATE = {})); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const wire_1 = require("../transport/wire"); const url_1 = require("url"); const fs = require("fs"); const promises_1 = require("./promises"); async function readLocalConfig(location) { const txt = await promises_1.promisify(fs.readFile)(location); return JSON.parse(txt.toString()); } async function downloadConfig(url) { const protocol = await Promise.resolve().then(() => require(url.protocol.slice(0, -1))); const res = await new Promise(async (resolve, reject) => { const request = protocol.get(url, (response) => { if (response.statusCode < 200 || response.statusCode > 299) { reject(new Error('Failed to load page, status code: ' + response.statusCode)); } const body = []; response.on('data', (chunk) => { body.push(chunk); }); response.on('end', () => resolve(body.join(''))); }); request.on('error', (err) => reject(err)); }); return JSON.parse(res); } async function loadConfig(config) { try { const x = url_1.parse(config.manifestUrl); return await downloadConfig(x); } catch (e) { try { return await readLocalConfig(config.manifestUrl); } catch (e) { throw new Error('Could not locate JSON at supplied manifestUrl'); } } } async function normalizeConfig(config) { const testThisConfig = config; if (wire_1.isExternalConfig(config)) { const loadedConfig = await loadConfig(config); testThisConfig.runtime = loadedConfig.runtime; if (typeof loadedConfig.assetsUrl === 'string') { testThisConfig.assetsUrl = loadedConfig.assetsUrl; } } return testThisConfig; } exports.normalizeConfig = normalizeConfig; async function validateConfig(config) { const normalized = await normalizeConfig(config); if (wire_1.isInternalConnectConfig(normalized)) { return normalized; } else { throw new Error('Invalid Config'); } } exports.validateConfig = validateConfig; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function promisify(func) { return (...args) => new Promise((resolve, reject) => { func(...args, (err, val) => err ? reject(err) : resolve(val)); }); } exports.promisify = promisify; async function promiseMap(arr, asyncF) { return Promise.all(arr.map(asyncF)); } exports.promiseMap = promiseMap; async function serial(arr) { const ret = []; for (const func of arr) { const next = await func(); ret.push(next); } return ret; } exports.serial = serial; async function promiseMapSerial(arr, func) { return serial(arr.map((value, index, array) => () => func(value, index, array))); } exports.promiseMapSerial = promiseMapSerial; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); class RefCoutner { constructor() { this.topicRefMap = new Map(); } incRefCount(key) { const refCount = this.topicRefMap.get(key); let returnCount; if (!refCount) { this.topicRefMap.set(key, 1); returnCount = 1; } else { const newRefCount = refCount + 1; returnCount = newRefCount; this.topicRefMap.set(key, newRefCount); } return returnCount; } decRefCount(key) { const refCount = this.topicRefMap.get(key); let returnCount; if (refCount) { const newRefCount = refCount - 1; this.topicRefMap.set(key, newRefCount); returnCount = newRefCount; } else { returnCount = -1; } return returnCount; } actOnFirst(key, firstAction, nonFirstAction = () => { }) { const numRefs = this.incRefCount(key); const isFirstRef = numRefs === 1; return isFirstRef ? firstAction() : nonFirstAction(); } actOnLast(key, lastAction, nonLastAction = () => { }) { const numRefs = this.decRefCount(key); const isLastRef = numRefs === 0; return isLastRef ? lastAction() : nonLastAction(); } } exports.default = RefCoutner; module.exports = function (args, opts) { if (!opts) opts = {}; var flags = { bools : {}, strings : {}, unknownFn: null }; if (typeof opts['unknown'] === 'function') { flags.unknownFn = opts['unknown']; } if (typeof opts['boolean'] === 'boolean' && opts['boolean']) { flags.allBools = true; } else { [].concat(opts['boolean']).filter(Boolean).forEach(function (key) { flags.bools[key] = true; }); } var aliases = {}; Object.keys(opts.alias || {}).forEach(function (key) { aliases[key] = [].concat(opts.alias[key]); aliases[key].forEach(function (x) { aliases[x] = [key].concat(aliases[key].filter(function (y) { return x !== y; })); }); }); [].concat(opts.string).filter(Boolean).forEach(function (key) { flags.strings[key] = true; if (aliases[key]) { flags.strings[aliases[key]] = true; } }); var defaults = opts['default'] || {}; var argv = { _ : [] }; Object.keys(flags.bools).forEach(function (key) { setArg(key, defaults[key] === undefined ? false : defaults[key]); }); var notFlags = []; if (args.indexOf('--') !== -1) { notFlags = args.slice(args.indexOf('--')+1); args = args.slice(0, args.indexOf('--')); } function argDefined(key, arg) { return (flags.allBools && /^--[^=]+$/.test(arg)) || flags.strings[key] || flags.bools[key] || aliases[key]; } function setArg (key, val, arg) { if (arg && flags.unknownFn && !argDefined(key, arg)) { if (flags.unknownFn(arg) === false) return; } var value = !flags.strings[key] && isNumber(val) ? Number(val) : val ; setKey(argv, key.split('.'), value); (aliases[key] || []).forEach(function (x) { setKey(argv, x.split('.'), value); }); } function setKey (obj, keys, value) { var o = obj; keys.slice(0,-1).forEach(function (key) { if (o[key] === undefined) o[key] = {}; o = o[key]; }); var key = keys[keys.length - 1]; if (o[key] === undefined || flags.bools[key] || typeof o[key] === 'boolean') { o[key] = value; } else if (Array.isArray(o[key])) { o[key].push(value); } else { o[key] = [ o[key], value ]; } } function aliasIsBoolean(key) { return aliases[key].some(function (x) { return flags.bools[x]; }); } for (var i = 0; i < args.length; i++) { var arg = args[i]; if (/^--.+=/.test(arg)) { // Using [\s\S] instead of . because js doesn't support the // 'dotall' regex modifier. See: // http://stackoverflow.com/a/1068308/13216 var m = arg.match(/^--([^=]+)=([\s\S]*)$/); var key = m[1]; var value = m[2]; if (flags.bools[key]) { value = value !== 'false'; } setArg(key, value, arg); } else if (/^--no-.+/.test(arg)) { var key = arg.match(/^--no-(.+)/)[1]; setArg(key, false, arg); } else if (/^--.+/.test(arg)) { var key = arg.match(/^--(.+)/)[1]; var next = args[i + 1]; if (next !== undefined && !/^-/.test(next) && !flags.bools[key] && !flags.allBools && (aliases[key] ? !aliasIsBoolean(key) : true)) { setArg(key, next, arg); i++; } else if (/^(true|false)$/.test(next)) { setArg(key, next === 'true', arg); i++; } else { setArg(key, flags.strings[key] ? '' : true, arg); } } else if (/^-[^-]+/.test(arg)) { var letters = arg.slice(1,-1).split(''); var broken = false; for (var j = 0; j < letters.length; j++) { var next = arg.slice(j+2); if (next === '-') { setArg(letters[j], next, arg) continue; } if (/[A-Za-z]/.test(letters[j]) && /=/.test(next)) { setArg(letters[j], next.split('=')[1], arg); broken = true; break; } if (/[A-Za-z]/.test(letters[j]) && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) { setArg(letters[j], next, arg); broken = true; break; } if (letters[j+1] && letters[j+1].match(/\W/)) { setArg(letters[j], arg.slice(j+2), arg); broken = true; break; } else { setArg(letters[j], flags.strings[letters[j]] ? '' : true, arg); } } var key = arg.slice(-1)[0]; if (!broken && key !== '-') { if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1]) && !flags.bools[key] && (aliases[key] ? !aliasIsBoolean(key) : true)) { setArg(key, args[i+1], arg); i++; } else if (args[i+1] && /true|false/.test(args[i+1])) { setArg(key, args[i+1] === 'true', arg); i++; } else { setArg(key, flags.strings[key] ? '' : true, arg); } } } else { if (!flags.unknownFn || flags.unknownFn(arg) !== false) { argv._.push( flags.strings['_'] || !isNumber(arg) ? arg : Number(arg) ); } if (opts.stopEarly) { argv._.push.apply(argv._, args.slice(i + 1)); break; } } } Object.keys(defaults).forEach(function (key) { if (!hasKey(argv, key.split('.'))) { setKey(argv, key.split('.'), defaults[key]); (aliases[key] || []).forEach(function (x) { setKey(argv, x.split('.'), defaults[key]); }); } }); if (opts['--']) { argv['--'] = new Array(); notFlags.forEach(function(key) { argv['--'].push(key); }); } else { notFlags.forEach(function(key) { argv._.push(key); }); } return argv; }; function hasKey (obj, keys) { var o = obj; keys.slice(0,-1).forEach(function (key) { o = (o[key] || {}); }); var key = keys[keys.length - 1]; return key in o; } function isNumber (x) { if (typeof x === 'number') return true; if (/^0x[0-9a-f]+$/i.test(x)) return true; return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x); } /*! * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ var fs = require('fs'); function Options(defaults) { var internalValues = {}; var values = this.value = {}; Object.keys(defaults).forEach(function(key) { internalValues[key] = defaults[key]; Object.defineProperty(values, key, { get: function() { return internalValues[key]; }, configurable: false, enumerable: true }); }); this.reset = function() { Object.keys(defaults).forEach(function(key) { internalValues[key] = defaults[key]; }); return this; }; this.merge = function(options, required) { options = options || {}; if (Object.prototype.toString.call(required) === '[object Array]') { var missing = []; for (var i = 0, l = required.length; i < l; ++i) { var key = required[i]; if (!(key in options)) { missing.push(key); } } if (missing.length > 0) { if (missing.length > 1) { throw new Error('options ' + missing.slice(0, missing.length - 1).join(', ') + ' and ' + missing[missing.length - 1] + ' must be defined'); } else throw new Error('option ' + missing[0] + ' must be defined'); } } Object.keys(options).forEach(function(key) { if (key in internalValues) { internalValues[key] = options[key]; } }); return this; }; this.copy = function(keys) { var obj = {}; Object.keys(defaults).forEach(function(key) { if (keys.indexOf(key) !== -1) { obj[key] = values[key]; } }); return obj; }; this.read = function(filename, cb) { if (typeof cb == 'function') { var self = this; fs.readFile(filename, function(error, data) { if (error) return cb(error); var conf = JSON.parse(data); self.merge(conf); cb(); }); } else { var conf = JSON.parse(fs.readFileSync(filename)); this.merge(conf); } return this; }; this.isDefined = function(key) { return typeof values[key] != 'undefined'; }; this.isDefinedAndNonNull = function(key) { return typeof values[key] != 'undefined' && values[key] !== null; }; Object.freeze(values); Object.freeze(this); } module.exports = Options; ALL_TESTS = $(shell find test/ -name '*.test.js') run-tests: @./node_modules/.bin/mocha \ -t 2000 \ $(TESTFLAGS) \ $(TESTS) test: @$(MAKE) NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests .PHONY: test { "_args": [ [ "options@0.0.6", "C:\\Users\\wenju\\Documents\\projects\\core" ] ], "_from": "options@0.0.6", "_id": "options@0.0.6", "_inBundle": false, "_integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", "_location": "/options", "_phantomChildren": {}, "_requested": { "type": "version", "registry": true, "raw": "options@0.0.6", "name": "options", "escapedName": "options", "rawSpec": "0.0.6", "saveSpec": null, "fetchSpec": "0.0.6" }, "_requiredBy": [ "/", "/ws" ], "_resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", "_spec": "0.0.6", "_where": "C:\\Users\\wenju\\Documents\\projects\\core", "author": { "name": "Einar Otto Stangvik", "email": "einaros@gmail.com", "url": "http://2x.io" }, "bugs": { "url": "https://github.com/einaros/options.js/issues" }, "dependencies": {}, "description": "A very light-weight in-code option parsers for node.js.", "devDependencies": { "mocha": "latest" }, "engines": { "node": ">=0.4.0" }, "homepage": "https://github.com/einaros/options.js#readme", "main": "lib/options", "name": "options", "repository": { "type": "git", "url": "git://github.com/einaros/options.js.git" }, "scripts": { "test": "make test" }, "version": "0.0.6" } # options.js # A very light-weight in-code option parsers for node.js. ## Usage ## ``` js var Options = require("options"); // Create an Options object function foo(options) { var default_options = { foo : "bar" }; // Create an option object with default value var opts = new Options(default_options); // Merge options opts = opts.merge(options); // Reset to default value opts.reset(); // Copy selected attributes out var seled_att = opts.copy("foo"); // Read json options from a file. opts.read("options.file"); // Sync opts.read("options.file", function(err){ // Async if(err){ // If error occurs console.log("File error."); }else{ // No error } }); // Attributes defined or not opts.isDefinedAndNonNull("foobar"); opts.isDefined("foobar"); } ``` ## License ## (The MIT License) Copyright (c) 2012 Einar Otto Stangvik <einaros@gmail.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. /* Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.*/ (function(a){function b(a){return a&&a.Object===Object?a:null}function c(a){for(var b=[],c=0,d=a.length;d>c;c++)b.push(a[c]);return b}function d(a){return function(){try{return a.apply(this,arguments)}catch(b){return Za.e=b,Za}}}function e(a){throw a}function f(a,b){if(_a&&b.stack&&"object"==typeof a&&null!==a&&a.stack&&-1===a.stack.indexOf(db)){for(var c=[],d=b;d;d=d.source)d.stack&&c.unshift(d.stack);c.unshift(a.stack);var e=c.join("\n"+db+"\n");a.stack=g(e)}}function g(a){for(var b=a.split("\n"),c=[],d=0,e=b.length;e>d;d++){var f=b[d];h(f)||i(f)||!f||c.push(f)}return c.join("\n")}function h(a){var b=k(a);if(!b)return!1;var c=b[0],d=b[1];return c===bb&&d>=cb&&Gg>=d}function i(a){return-1!==a.indexOf("(module.js:")||-1!==a.indexOf("(node.js:")}function j(){if(_a)try{throw new Error}catch(a){var b=a.stack.split("\n"),c=b[0].indexOf("@")>0?b[1]:b[2],d=k(c);if(!d)return;return bb=d[0],d[1]}}function k(a){var b=/at .+ \((.+):(\d+):(?:\d+)\)$/.exec(a);if(b)return[b[1],Number(b[2])];var c=/at ([^ ]+):(\d+):(?:\d+)$/.exec(a);if(c)return[c[1],Number(c[2])];var d=/.*@(.+):(\d+)$/.exec(a);return d?[d[1],Number(d[2])]:void 0}function l(b,c,d,e,f,g){var h=Sb(b),i=h.length,j=Sb(c),k=j.length;if(i!==k&&!e)return!1;for(var l,m=i;m--;)if(l=h[m],!(e?l in c:Pb.call(c,l)))return!1;for(var n=e;++m-1&&a%1===0&&Rb>=a}function p(a){return n(a)&&o(a.length)&&!!Nb[Qb.call(a)]}function q(a,b){for(var c=-1,d=a.length;++ci))return!1;for(;++hd;d++)c[d]=b();return c}function v(a,b){this.id=a,this.value=b}function w(a,b){this.scheduler=a,this.disposable=b,this.isDisposed=!1}function x(a,b){b.isDisposed||(b.isDisposed=!0,b.disposable.dispose())}function y(a){this._s=a,this.isDisposed=!1}function z(a){this._s=a}function A(a){this._s=a,this._l=a.length,this._i=0}function B(a){this._a=a}function C(a){this._a=a,this._l=G(a),this._i=0}function D(a){return"number"==typeof a&&Pa.isFinite(a)}function E(b){var c,d=b[kb];if(!d&&"string"==typeof b)return c=new z(b),c[kb]();if(!d&&b.length!==a)return c=new B(b),c[kb]();if(!d)throw new TypeError("Object is not iterable");return b[kb]()}function F(a){var b=+a;return 0===b?b:isNaN(b)?b:0>b?-1:1}function G(a){var b=+a.length;return isNaN(b)?0:0!==b&&D(b)?(b=F(b)*Math.floor(Math.abs(b)),0>=b?0:b>ld?ld:b):b}function H(a,b){return pc(a)||(a=wc),new nd(b,a)}function I(a,b){this.observer=a,this.parent=b}function J(a,b){return a.amb(b)}function K(){return!1}function L(){for(var a=arguments.length,b=new Array(a),c=0;a>c;c++)b[c]=arguments[c];return b}function K(){return!1}function L(){for(var a=arguments.length,b=new Array(a),c=0;a>c;c++)b[c]=arguments[c];return b}function K(){return!1}function M(){return[]}function K(){return!1}function M(){return[]}function L(){for(var a=arguments.length,b=new Array(a),c=0;a>c;c++)b[c]=arguments[c];return b}function N(a){return function(b){return a.subscribe(b)}}function O(a){return a.toArray()}function P(a){return a.length>0}function Q(a){return{"@@iterator":function(){return{next:function(){return{done:!1,value:a}}}}}}function Q(a){return{"@@iterator":function(){return{next:function(){return{done:!1,value:a}}}}}}function R(a,b,c){var d=ob(b,c,3);return a.map(function(b,c){var e=d(b,c,a);return Xa(e)&&(e=dd(e)),(nb(e)||mb(e))&&(e=md(e)),e}).concatAll()}function S(a,b,c){for(var d=0,e=a.length;e>d;d++)if(c(a[d],b))return d;return-1}function T(a){this.comparer=a,this.set=[]}function U(b,c){return function(d){for(var e=d,f=0;c>f;f++){var g=e[b[f]];if("undefined"==typeof g)return a;e=g}return e}}function V(a){if(0===a.length)throw new eb;return a[0]}function W(a,b,c,d){var e=ob(b,c,3);return new xg(function(b){return a.subscribe(new gf(b,a,e,d))},a)}function X(a){return a?Sc.isObservable(a)?a:Xa(a)?Sc.fromPromise(a):aa(a)||_(a)?qf.call(this,a):Ya(a)?$.call(this,a):nb(a)||mb(a)?Y.call(this,a):ba(a)?Z.call(this,a):a:a}function Y(a){return Sc.from(a).concatMap(function(a){return Sc.isObservable(a)||ba(a)?X.call(null,a):Qa.Observable.just(a)}).toArray()}function Z(b){function c(b,c){d[c]=a,f.push(b.map(function(a){d[c]=a}))}for(var d=new b.constructor,e=Object.keys(b),f=[],g=0,h=e.length;h>g;g++){var i=e[g],j=X.call(this,b[i]);j&&Sc.isObservable(j)?c(j,i):d[i]=b[i]}return Sc.forkJoin.apply(Sc,f).map(function(){return d})}function $(a){var b=this;return new xg(function(c){a.call(b,function(){var a=arguments[0],b=arguments[1];if(a)return c.onError(a);if(arguments.length>2){for(var d=[],e=1,f=arguments.length;f>e;e++)d.push(arguments[e]);b=d}c.onNext(b),c.onCompleted()})})}function _(a){return Ya(a.next)&&Ya(a["throw"])}function aa(a){var b=a.constructor;return b?"GeneratorFunction"===b.name||"GeneratorFunction"===b.displayName?!0:_(b.prototype):!1}function ba(a){return Object==a.constructor}function ca(a,b,c,d){var e=new Cg;return d.push(da(e,b,c)),a.apply(b,d),e.asObservable()}function da(a,b,c){return function(){for(var d=arguments.length,e=new Array(d),f=0;d>f;f++)e[f]=arguments[f];if(Ya(c)){if(e=$a(c).apply(b,e),e===Za)return a.onError(e.e);a.onNext(e)}else e.length<=1?a.onNext(e[0]):a.onNext(e);a.onCompleted()}}function ea(a,b,c,d){var e=new Cg;return d.push(fa(e,b,c)),a.apply(b,d),e.asObservable()}function fa(a,b,c){return function(){var d=arguments[0];if(d)return a.onError(d);for(var e=arguments.length,f=[],g=1;e>g;g++)f[g-1]=arguments[g];if(Ya(c)){var f=$a(c).apply(b,f);if(f===Za)return a.onError(f.e);a.onNext(f)}else f.length<=1?a.onNext(f[0]):a.onNext(f);a.onCompleted()}}function ga(a){return Pa.StaticNodeList?a instanceof Pa.StaticNodeList||a instanceof Pa.NodeList:"[object NodeList]"===Object.prototype.toString.call(a)}function ha(a,b,c){this._e=a,this._n=b,this._fn=c,this._e.addEventListener(this._n,this._fn,!1),this.isDisposed=!1}function ia(a,b,c){var d=new _b,e=Object.prototype.toString.call(a);if(ga(a)||"[object HTMLCollection]"===e)for(var f=0,g=a.length;g>f;f++)d.add(ia(a.item(f),b,c));else a&&d.add(new ha(a,b,c));return d}function ja(a,b,c){return new xg(function(d){function e(a,b){if(j[b]=a,g[b]=!0,h||(h=g.every(Sa))){if(f)return d.onError(f);var e=$a(c).apply(null,j);if(e===Za)return d.onError(e.e);d.onNext(e)}i&&j[1]&&d.onCompleted()}var f,g=[!1,!1],h=!1,i=!1,j=new Array(2);return new jc(a.subscribe(function(a){e(a,0)},function(a){j[1]?d.onError(a):f=a},function(){i=!0,j[1]&&d.onCompleted()}),b.subscribe(function(a){e(a,1)},function(a){d.onError(a)},function(){i=!0,e(!0,1)}))},a)}function O(a){return a.toArray()}function ka(a,b){return a.groupJoin(this,b,jd,function(a,b){return b})}function la(a){var b=this;return new xg(function(c){var d=new Bg,e=new _b,f=new lc(e);return c.onNext(Yb(d,f)),e.add(b.subscribe(function(a){d.onNext(a)},function(a){d.onError(a),c.onError(a)},function(){d.onCompleted(),c.onCompleted()})),Xa(a)&&(a=dd(a)),e.add(a.subscribe(function(a){d.onCompleted(),d=new Bg,c.onNext(Yb(d,f))},function(a){d.onError(a),c.onError(a)},function(){d.onCompleted(),c.onCompleted()})),f},b)}function ma(a){var b=this;return new xg(function(c){function d(){var b;try{b=a()}catch(f){return void c.onError(f)}Xa(b)&&(b=dd(b));var i=new hc;e.setDisposable(i),i.setDisposable(b.take(1).subscribe(Ra,function(a){h.onError(a),c.onError(a)},function(){h.onCompleted(),h=new Bg,c.onNext(Yb(h,g)),d()}))}var e=new ic,f=new _b(e),g=new lc(f),h=new Bg;return c.onNext(Yb(h,g)),f.add(b.subscribe(function(a){h.onNext(a)},function(a){h.onError(a),c.onError(a)},function(){h.onCompleted(),c.onCompleted()})),d(),g},b)}function na(a,b){return new Hf(a,b)}function L(){for(var a=arguments.length,b=new Array(a),c=0;a>c;c++)b[c]=arguments[c];return b}function oa(a){this.patterns=a}function pa(a,b){this.expression=a,this.selector=b}function qa(a){return function(b){a.onError(b)}}function ra(a,b){return function(){var c=$a(a.selector).apply(a,arguments);return c===Za?b.onError(c.e):void b.onNext(c)}}function sa(a,b,c){var d=a.get(b);if(!d){var e=new Pf(b,c);return a.set(b,e),e}return d}function ta(a,b,c){this.joinObserverArray=a,this.onNext=b,this.onCompleted=c,this.joinObservers=new Of;for(var d=0,e=this.joinObserverArray.length;e>d;d++){var f=this.joinObserverArray[d];this.joinObservers.set(f,f)}}function ua(a,b){return new Qf(a,b)}function va(a,b,c){return new xg(function(d){var e=a,f=oc(b);return c.scheduleRecursiveFuture(0,e,function(a,b){if(f>0){var g=c.now();e=new Date(e.getTime()+f),e.getTime()<=g&&(e=new Date(g+f))}d.onNext(a),b(a+1,new Date(e))})})}function wa(a,b,c){return a===b?new xg(function(a){return c.schedulePeriodic(0,b,function(b){return a.onNext(b),b+1})}):gd(function(){return va(new Date(c.now()+a),b,c)})}function xa(a,b,c){return new xg(function(d){var e,f=!1,g=new ic,h=null,i=[],j=!1;return e=a.materialize().timestamp(c).subscribe(function(a){var e,k;"E"===a.value.kind?(i=[],i.push(a),h=a.value.error,k=!j):(i.push({value:a.value,timestamp:a.timestamp+b}),k=!f,f=!0),k&&(null!==h?d.onError(h):(e=new hc,g.setDisposable(e),e.setDisposable(c.scheduleRecursiveFuture(null,b,function(a,b){var e,g,k,l;if(null===h){j=!0;do k=null,i.length>0&&i[0].timestamp-c.now()<=0&&(k=i.shift().value),null!==k&&k.accept(d);while(null!==k);l=!1,g=0,i.length>0?(l=!0,g=Math.max(0,i[0].timestamp-c.now())):f=!1,e=h,j=!1,null!==e?d.onError(e):l&&b(null,g)}}))))}),new jc(e,g)},a)}function ya(a,b,c){return gd(function(){return xa(a,b-c.now(),c)})}function za(a,b,c){var d,e;return Ya(b)?e=b:(d=b,e=c),new xg(function(b){function c(){i.setDisposable(a.subscribe(function(a){var c=$a(e)(a);if(c===Za)return b.onError(c.e);var d=new hc;g.add(d),d.setDisposable(c.subscribe(function(){b.onNext(a),g.remove(d),f()},function(a){b.onError(a)},function(){b.onNext(a),g.remove(d),f()}))},function(a){b.onError(a)},function(){h=!0,i.dispose(),f()}))}function f(){h&&0===g.length&&b.onCompleted()}var g=new _b,h=!1,i=new ic;return d?i.setDisposable(d.subscribe(c,function(a){b.onError(a)},c)):c(),new jc(i,g)},a)}function Aa(a,b){return new xg(function(c){var d,e=!1,f=new ic,g=0,h=a.subscribe(function(a){var h=$a(b)(a);if(h===Za)return c.onError(h.e);Xa(h)&&(h=dd(h)),e=!0,d=a,g++;var i=g,j=new hc;f.setDisposable(j),j.setDisposable(h.subscribe(function(){e&&g===i&&c.onNext(d),e=!1,j.dispose()},function(a){c.onError(a)},function(){e&&g===i&&c.onNext(d),e=!1,j.dispose()}))},function(a){f.dispose(),c.onError(a),e=!1,g++},function(){f.dispose(),e&&c.onNext(d),c.onCompleted(),e=!1,g++});return new jc(h,f)},a)}function O(a){return a.toArray()}function O(a){return a.toArray()}function Ba(a,b,c,d){return Ya(b)&&(d=c,c=b,b=sd()),Sc.isObservable(d)||(d=yd(new _f)),new xg(function(e){function f(a){function b(){return l=c===k}var c=k,f=new hc;i.setDisposable(f),f.setDisposable(a.subscribe(function(){b()&&h.setDisposable(d.subscribe(e)),f.dispose()},function(a){b()&&e.onError(a)},function(){b()&&h.setDisposable(d.subscribe(e))}))}function g(){var a=!l;return a&&k++,a}var h=new ic,i=new ic,j=new hc;h.setDisposable(j);var k=0,l=!1;return f(b),j.setDisposable(a.subscribe(function(a){if(g()){e.onNext(a);var b=$a(c)(a);if(b===Za)return e.onError(b.e);f(Xa(b)?dd(b):b)}},function(a){g()&&e.onError(a)},function(){g()&&e.onCompleted()})),new jc(h,i)},a)}function Ca(a,b,c,d){return pc(c)&&(d=c,c=yd(new _f)),c instanceof Error&&(c=yd(c)),pc(d)||(d=Bc),Sc.isObservable(c)||(c=yd(new _f)),new xg(function(e){function f(){var a=g;k.setDisposable(d.scheduleFuture(null,b,function(){j=g===a,j&&(Xa(c)&&(c=dd(c)),i.setDisposable(c.subscribe(e)))}))}var g=0,h=new hc,i=new ic,j=!1,k=new ic;return i.setDisposable(h),f(),h.setDisposable(a.subscribe(function(a){j||(g++,e.onNext(a),f())},function(a){j||(g++,e.onError(a))},function(){j||(g++,e.onCompleted())})),new jc(i,k)},a)}function Da(a){return{"@@transducer/init":function(){return a},"@@transducer/step":function(a,b){return a.onNext(b)},"@@transducer/result":function(a){return a.onCompleted()}}}function Ea(a){this.predicate=a}function Fa(a){this.predicate=a}function Ga(a,b){var c=this;this.scheduler=a,this.messages=b,this.subscriptions=[],this.observers=[];for(var d=0,e=this.messages.length;e>d;d++){var f=this.messages[d],g=f.value;!function(b){a.scheduleAbsolute(null,f.time,function(){for(var a=c.observers.slice(0),d=0,e=a.length;e>d;d++)b.accept(a[d]);return dc})}(g)}}var Ha={"function":!0,object:!0},Ia=Ha[typeof exports]&&exports&&!exports.nodeType?exports:null,Ja=Ha[typeof module]&&module&&!module.nodeType?module:null,Ka=b(Ia&&Ja&&"object"==typeof global&&global),La=b(Ha[typeof self]&&self),Ma=b(Ha[typeof window]&&window),Na=Ja&&Ja.exports===Ia?Ia:null,Oa=b(Ha[typeof this]&&this),Pa=Ka||Ma!==(Oa&&Oa.window)&&Ma||La||Oa||Function("return this")(),Qa={internals:{},config:{Promise:Pa.Promise},helpers:{}},Ra=Qa.helpers.noop=function(){},Sa=Qa.helpers.identity=function(a){return a},Ta=Qa.helpers.defaultNow=Date.now,Ua=Qa.helpers.defaultComparer=function(a,b){return Vb(a,b)},Va=Qa.helpers.defaultSubComparer=function(a,b){return a>b?1:b>a?-1:0},Wa=(Qa.helpers.defaultKeySerializer=function(a){return a.toString()},Qa.helpers.defaultError=function(a){throw a}),Xa=Qa.helpers.isPromise=function(a){return!!a&&"function"!=typeof a.subscribe&&"function"==typeof a.then},Ya=Qa.helpers.isFunction=function(){var a=function(a){return"function"==typeof a||!1};return a(/x/)&&(a=function(a){return"function"==typeof a&&"[object Function]"==toString.call(a)}),a}(),Za={e:{}},$a=Qa.internals.tryCatch=function(a){if(!Ya(a))throw new TypeError("fn must be a function");return d(a)};Qa.config.longStackSupport=!1;var _a=!1,ab=$a(function(){throw new Error})();_a=!!ab.e&&!!ab.e.stack;var bb,cb=j(),db="From previous event:",eb=Qa.EmptyError=function(){this.message="Sequence contains no elements.",Error.call(this)};eb.prototype=Object.create(Error.prototype),eb.prototype.name="EmptyError";var fb=Qa.ObjectDisposedError=function(){this.message="Object has been disposed",Error.call(this)};fb.prototype=Object.create(Error.prototype),fb.prototype.name="ObjectDisposedError";var gb=Qa.ArgumentOutOfRangeError=function(){this.message="Argument out of range",Error.call(this)};gb.prototype=Object.create(Error.prototype),gb.prototype.name="ArgumentOutOfRangeError";var hb=Qa.NotSupportedError=function(a){this.message=a||"This operation is not supported",Error.call(this)};hb.prototype=Object.create(Error.prototype),hb.prototype.name="NotSupportedError";var ib=Qa.NotImplementedError=function(a){this.message=a||"This operation is not implemented",Error.call(this)};ib.prototype=Object.create(Error.prototype),ib.prototype.name="NotImplementedError";var jb=Qa.helpers.notImplemented=function(){throw new ib},kb=(Qa.helpers.notSupported=function(){throw new hb},"function"==typeof Symbol&&Symbol.iterator||"_es6shim_iterator_");Pa.Set&&"function"==typeof(new Pa.Set)["@@iterator"]&&(kb="@@iterator");var lb=Qa.doneEnumerator={done:!0,value:a},mb=Qa.helpers.isIterable=function(b){return b&&b[kb]!==a},nb=Qa.helpers.isArrayLike=function(b){return b&&b.length!==a};Qa.helpers.iterator=kb;var ob=Qa.internals.bindCallback=function(a,b,c){if("undefined"==typeof b)return a;switch(c){case 0:return function(){return a.call(b)};case 1:return function(c){return a.call(b,c)};case 2:return function(c,d){return a.call(b,c,d)};case 3:return function(c,d,e){return a.call(b,c,d,e)}}return function(){return a.apply(b,arguments)}},pb=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],qb=(pb.length,"[object Arguments]"),rb="[object Array]",sb="[object Boolean]",tb="[object Date]",ub="[object Error]",vb="[object Function]",wb="[object Map]",xb="[object Number]",yb="[object Object]",zb="[object RegExp]",Ab="[object Set]",Bb="[object String]",Cb="[object WeakMap]",Db="[object ArrayBuffer]",Eb="[object Float32Array]",Fb="[object Float64Array]",Gb="[object Int8Array]",Hb="[object Int16Array]",Ib="[object Int32Array]",Jb="[object Uint8Array]",Kb="[object Uint8ClampedArray]",Lb="[object Uint16Array]",Mb="[object Uint32Array]",Nb={};Nb[Eb]=Nb[Fb]=Nb[Gb]=Nb[Hb]=Nb[Ib]=Nb[Jb]=Nb[Kb]=Nb[Lb]=Nb[Mb]=!0,Nb[qb]=Nb[rb]=Nb[Db]=Nb[sb]=Nb[tb]=Nb[ub]=Nb[vb]=Nb[wb]=Nb[xb]=Nb[yb]=Nb[zb]=Nb[Ab]=Nb[Bb]=Nb[Cb]=!1;var Ob=Object.prototype,Pb=Ob.hasOwnProperty,Qb=Ob.toString,Rb=Math.pow(2,53)-1,Sb=Object.keys||function(){var a=Object.prototype.hasOwnProperty,b=!{toString:null}.propertyIsEnumerable("toString"),c=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],d=c.length;return function(e){if("object"!=typeof e&&("function"!=typeof e||null===e))throw new TypeError("Object.keys called on non-object");var f,g,h=[];for(f in e)a.call(e,f)&&h.push(f);if(b)for(g=0;d>g;g++)a.call(e,c[g])&&h.push(c[g]);return h}}(),ba=Qa.internals.isObject=function(a){var b=typeof a;return!!a&&("object"===b||"function"===b)},Tb=function(){try{Object({toString:0}+"")}catch(a){return function(){return!1}}return function(a){return"function"!=typeof a.toString&&"string"==typeof(a+"")}}(),Ub=Array.isArray||function(a){return n(a)&&o(a.length)&&Qb.call(a)===rb},Vb=Qa.internals.isEqual=function(a,b){return t(a,b)},Wb=({}.hasOwnProperty,Array.prototype.slice,Qa.internals.inherits=function(a,b){function c(){this.constructor=a}c.prototype=b.prototype,a.prototype=new c}),Xb=Qa.internals.addProperties=function(a){for(var b=[],c=1,d=arguments.length;d>c;c++)b.push(arguments[c]);for(var e=0,f=b.length;f>e;e++){var g=b[e];for(var h in g)a[h]=g[h]}},Yb=Qa.internals.addRef=function(a,b){return new xg(function(c){return new jc(b.getDisposable(),a.subscribe(c))})};v.prototype.compareTo=function(a){var b=this.value.compareTo(a.value);return 0===b&&(b=this.id-a.id),b};var Zb=Qa.internals.PriorityQueue=function(a){this.items=new Array(a),this.length=0},$b=Zb.prototype;$b.isHigherPriority=function(a,b){return this.items[a].compareTo(this.items[b])<0},$b.percolate=function(a){if(!(a>=this.length||0>a)){var b=a-1>>1;if(!(0>b||b===a)&&this.isHigherPriority(a,b)){var c=this.items[a];this.items[a]=this.items[b],this.items[b]=c,this.percolate(b)}}},$b.heapify=function(a){if(+a||(a=0),!(a>=this.length||0>a)){var b=2*a+1,c=2*a+2,d=a;if(ba;a++)c[a]=arguments[a];this.disposables=c,this.isDisposed=!1,this.length=c.length},ac=_b.prototype;ac.add=function(a){this.isDisposed?a.dispose():(this.disposables.push(a),this.length++)},ac.remove=function(a){var b=!1;if(!this.isDisposed){var c=this.disposables.indexOf(a);-1!==c&&(b=!0,this.disposables.splice(c,1),this.length--,a.dispose())}return b},ac.dispose=function(){if(!this.isDisposed){this.isDisposed=!0;for(var a=this.disposables.length,b=new Array(a),c=0;a>c;c++)b[c]=this.disposables[c];for(this.disposables=[],this.length=0,c=0;a>c;c++)b[c].dispose()}};var bc=Qa.Disposable=function(a){this.isDisposed=!1,this.action=a||Ra};bc.prototype.dispose=function(){this.isDisposed||(this.action(),this.isDisposed=!0)};var cc=bc.create=function(a){return new bc(a)},dc=bc.empty={dispose:Ra},ec=bc.isDisposable=function(a){return a&&Ya(a.dispose)},fc=bc.checkDisposed=function(a){if(a.isDisposed)throw new fb},gc=bc._fixup=function(a){return ec(a)?a:dc},hc=Qa.SingleAssignmentDisposable=function(){this.isDisposed=!1,this.current=null};hc.prototype.getDisposable=function(){return this.current},hc.prototype.setDisposable=function(a){if(this.current)throw new Error("Disposable has already been assigned");var b=this.isDisposed;!b&&(this.current=a),b&&a&&a.dispose()},hc.prototype.dispose=function(){if(!this.isDisposed){this.isDisposed=!0;var a=this.current;this.current=null,a&&a.dispose()}};var ic=Qa.SerialDisposable=function(){this.isDisposed=!1,this.current=null};ic.prototype.getDisposable=function(){return this.current},ic.prototype.setDisposable=function(a){var b=this.isDisposed;if(!b){var c=this.current;this.current=a}c&&c.dispose(),b&&a&&a.dispose()},ic.prototype.dispose=function(){if(!this.isDisposed){this.isDisposed=!0;var a=this.current;this.current=null}a&&a.dispose()};var jc=Qa.BinaryDisposable=function(a,b){this._first=a,this._second=b,this.isDisposed=!1};jc.prototype.dispose=function(){if(!this.isDisposed){this.isDisposed=!0;var a=this._first;this._first=null,a&&a.dispose();var b=this._second;this._second=null,b&&b.dispose()}};var kc=Qa.NAryDisposable=function(a){this._disposables=a,this.isDisposed=!1};kc.prototype.dispose=function(){if(!this.isDisposed){this.isDisposed=!0;for(var a=0,b=this._disposables.length;b>a;a++)this._disposables[a].dispose();this._disposables.length=0}};var lc=Qa.RefCountDisposable=function(){function a(a){this.disposable=a,this.disposable.count++,this.isInnerDisposed=!1}function b(a){this.underlyingDisposable=a,this.isDisposed=!1,this.isPrimaryDisposed=!1,this.count=0}return a.prototype.dispose=function(){this.disposable.isDisposed||this.isInnerDisposed||(this.isInnerDisposed=!0,this.disposable.count--,0===this.disposable.count&&this.disposable.isPrimaryDisposed&&(this.disposable.isDisposed=!0,this.disposable.underlyingDisposable.dispose()))},b.prototype.dispose=function(){this.isDisposed||this.isPrimaryDisposed||(this.isPrimaryDisposed=!0,0===this.count&&(this.isDisposed=!0,this.underlyingDisposable.dispose()))},b.prototype.getDisposable=function(){return this.isDisposed?dc:new a(this)},b}();w.prototype.dispose=function(){this.scheduler.schedule(this,x)};var mc=Qa.internals.ScheduledItem=function(a,b,c,d,e){this.scheduler=a,this.state=b,this.action=c,this.dueTime=d,this.comparer=e||Va,this.disposable=new hc};mc.prototype.invoke=function(){this.disposable.setDisposable(this.invokeCore())},mc.prototype.compareTo=function(a){return this.comparer(this.dueTime,a.dueTime)},mc.prototype.isCancelled=function(){return this.disposable.isDisposed},mc.prototype.invokeCore=function(){return gc(this.action(this.scheduler,this.state))};var nc=Qa.Scheduler=function(){function a(){}a.isScheduler=function(b){return b instanceof a};var b=a.prototype;return b.schedule=function(a,b){throw new ib},b.scheduleFuture=function(b,c,d){var e=c;return e instanceof Date&&(e-=this.now()),e=a.normalize(e),0===e?this.schedule(b,d):this._scheduleFuture(b,e,d)},b._scheduleFuture=function(a,b,c){throw new ib},a.now=Ta,a.prototype.now=Ta,a.normalize=function(a){return 0>a&&(a=0),a},a}(),oc=nc.normalize,pc=nc.isScheduler;!function(a){function b(a,b){function c(b){function d(a,b){return g?f.remove(i):h=!0,e(b,c),dc}var g=!1,h=!1,i=a.schedule(b,d);h||(f.add(i),g=!0)}var d=b[0],e=b[1],f=new _b;return e(d,c),f}function c(a,b){function c(b,d){function g(a,b){return h?f.remove(j):i=!0,e(b,c),dc}var h=!1,i=!1,j=a.scheduleFuture(b,d,g);i||(f.add(j),h=!0)}var d=b[0],e=b[1],f=new _b;return e(d,c),f}a.scheduleRecursive=function(a,c){return this.schedule([a,c],b)},a.scheduleRecursiveFuture=function(a,b,d){return this.scheduleFuture([a,d],b,c)}}(nc.prototype),function(a){a.schedulePeriodic=function(a,b,c){if("undefined"==typeof Pa.setInterval)throw new hb;b=oc(b);var d=a,e=Pa.setInterval(function(){d=c(d)},b);return cc(function(){Pa.clearInterval(e)})}}(nc.prototype),function(a){a.catchError=a["catch"]=function(a){return new Cc(this,a)}}(nc.prototype);var qc,rc,sc=Qa.internals.SchedulePeriodicRecursive=function(){function a(a){return function(b,c){c(0,a._period);var d=$a(a._action)(a._state);d===Za&&(a._cancel.dispose(),e(d.e)),a._state=d}}function b(a,b,c,d){this._scheduler=a,this._state=b,this._period=c,this._action=d}return b.prototype.start=function(){var b=new hc;return this._cancel=b,b.setDisposable(this._scheduler.scheduleRecursiveFuture(0,this._period,a(this))),b},b}(),tc=function(a){function b(){a.call(this)}return Wb(b,a),b.prototype.schedule=function(a,b){return gc(b(this,a))},b}(nc),uc=nc.immediate=new tc,vc=function(a){function b(){for(;d.length>0;){var a=d.dequeue();!a.isCancelled()&&a.invoke()}}function c(){a.call(this)}var d;return Wb(c,a),c.prototype.schedule=function(a,c){var f=new mc(this,a,c,this.now());if(d)d.enqueue(f);else{d=new Zb(4),d.enqueue(f);var g=$a(b)();d=null,g===Za&&e(g.e)}return f.disposable},c.prototype.scheduleRequired=function(){return!d},c}(nc),wc=nc.currentThread=new vc,xc=function(){var a,b=Ra;if(Pa.setTimeout)a=Pa.setTimeout,b=Pa.clearTimeout;else{if(!Pa.WScript)throw new hb;a=function(a,b){Pa.WScript.Sleep(b),a()}}return{setTimeout:a,clearTimeout:b}}(),yc=xc.setTimeout,zc=xc.clearTimeout;!function(){function a(b){if(f)yc(function(){a(b)},0);else{var c=d[b];if(c){f=!0;var g=$a(c)();rc(b),f=!1,g===Za&&e(g.e)}}}function b(){if(!Pa.postMessage||Pa.importScripts)return!1;var a=!1,b=Pa.onmessage;return Pa.onmessage=function(){a=!0},Pa.postMessage("","*"),Pa.onmessage=b,a}var c=1,d={},f=!1;rc=function(a){delete d[a]};var g=new RegExp("^"+String(toString).replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$"),h="function"==typeof(h=Ka&&Na&&Ka.setImmediate)&&!g.test(h)&&h;if(Ya(h))qc=function(b){var e=c++;return d[e]=b,h(function(){a(e)}),e};else if("undefined"!=typeof process&&"[object process]"==={}.toString.call(process))qc=function(b){var e=c++;return d[e]=b,process.nextTick(function(){a(e)}),e};else if(b()){var i="ms.rx.schedule"+Math.random(),j=function(b){"string"==typeof b.data&&b.data.substring(0,i.length)===i&&a(b.data.substring(i.length))};Pa.addEventListener("message",j,!1),qc=function(a){var b=c++;return d[b]=a,Pa.postMessage(i+b,"*"),b}}else if(Pa.MessageChannel){var k=new Pa.MessageChannel;k.port1.onmessage=function(b){a(b.data)},qc=function(a){var b=c++;return d[b]=a,k.port2.postMessage(b),b}}else qc="document"in Pa&&"onreadystatechange"in Pa.document.createElement("script")?function(b){var e=Pa.document.createElement("script"),f=c++;return d[f]=b,e.onreadystatechange=function(){a(f),e.onreadystatechange=null,e.parentNode.removeChild(e),e=null},Pa.document.documentElement.appendChild(e),f}:function(b){var e=c++;return d[e]=b,yc(function(){a(e)},0),e}}();var Ac=function(a){function b(){a.call(this)}function c(a,b,c,d){return function(){a.setDisposable(bc._fixup(b(c,d)))}}function d(a){this._id=a,this.isDisposed=!1}function e(a){this._id=a,this.isDisposed=!1}function f(a,b,c){return function(){b(a,c)}}return Wb(b,a),d.prototype.dispose=function(){this.isDisposed||(this.isDisposed=!0,rc(this._id))},e.prototype.dispose=function(){this.isDisposed||(this.isDisposed=!0,zc(this._id))},b.prototype.schedule=function(a,b){var e=new hc,f=qc(c(e,b,this,a));return new jc(e,new d(f))},b.prototype._scheduleFuture=function(a,b,d){if(0===b)return this.schedule(a,d);var f=new hc,g=yc(c(f,d,this,a),b);return new jc(f,new e(g))},b.prototype.scheduleLongRunning=function(a,b){var c=cc(Ra);return qc(f(a,b,c)),c},b}(nc),Bc=nc["default"]=nc.async=new Ac,Cc=function(a){function b(b,c){this._scheduler=b,this._handler=c,this._recursiveOriginal=null,this._recursiveWrapper=null,a.call(this)}return Wb(b,a),b.prototype.schedule=function(a,b){return this._scheduler.schedule(a,this._wrap(b))},b.prototype._scheduleFuture=function(a,b,c){return this._scheduler.schedule(a,b,this._wrap(c))},b.prototype.now=function(){return this._scheduler.now()},b.prototype._clone=function(a){return new b(a,this._handler)},b.prototype._wrap=function(a){var b=this;return function(c,d){var f=$a(a)(b._getRecursiveWrapper(c),d);return f===Za?(b._handler(f.e)||e(f.e),dc):gc(f)}},b.prototype._getRecursiveWrapper=function(a){if(this._recursiveOriginal!==a){this._recursiveOriginal=a;var b=this._clone(a);b._recursiveOriginal=a,b._recursiveWrapper=b,this._recursiveWrapper=b}return this._recursiveWrapper},b.prototype.schedulePeriodic=function(a,b,c){var d=this,f=!1,g=new hc;return g.setDisposable(this._scheduler.schedulePeriodic(a,b,function(a){if(f)return null;var b=$a(c)(a);return b===Za?(f=!0,d._handler(b.e)||e(b.e),g.dispose(),null):b})),g},b}(nc),Dc=Qa.Notification=function(){function a(){}return a.prototype._accept=function(a,b,c){throw new ib},a.prototype._acceptObserver=function(a,b,c){throw new ib},a.prototype.accept=function(a,b,c){return a&&"object"==typeof a?this._acceptObserver(a):this._accept(a,b,c)},a.prototype.toObservable=function(a){var b=this;return pc(a)||(a=uc),new xg(function(c){return a.schedule(b,function(a,b){b._acceptObserver(c),"N"===b.kind&&c.onCompleted()})})},a}(),Ec=function(a){function b(a){this.value=a,this.kind="N"}return Wb(b,a),b.prototype._accept=function(a){return a(this.value)},b.prototype._acceptObserver=function(a){return a.onNext(this.value)},b.prototype.toString=function(){return"OnNext("+this.value+")"},b}(Dc),Fc=function(a){function b(a){this.error=a,this.kind="E"}return Wb(b,a),b.prototype._accept=function(a,b){return b(this.error)},b.prototype._acceptObserver=function(a){return a.onError(this.error)},b.prototype.toString=function(){return"OnError("+this.error+")"},b}(Dc),Gc=function(a){function b(){this.kind="C"}return Wb(b,a),b.prototype._accept=function(a,b,c){return c()},b.prototype._acceptObserver=function(a){return a.onCompleted()},b.prototype.toString=function(){return"OnCompleted()"},b}(Dc),Hc=Dc.createOnNext=function(a){return new Ec(a)},Ic=Dc.createOnError=function(a){return new Fc(a)},Jc=Dc.createOnCompleted=function(){return new Gc},Kc=Qa.Observer=function(){};Kc.prototype.toNotifier=function(){var a=this;return function(b){return b.accept(a)}},Kc.prototype.asObserver=function(){var a=this;return new Oc(function(b){a.onNext(b)},function(b){a.onError(b)},function(){a.onCompleted()})},Kc.prototype.checked=function(){return new Pc(this)};var Lc=Kc.create=function(a,b,c){return a||(a=Ra),b||(b=Wa),c||(c=Ra),new Oc(a,b,c)};Kc.fromNotifier=function(a,b){var c=ob(a,b,1);return new Oc(function(a){return c(Hc(a))},function(a){return c(Ic(a))},function(){return c(Jc())})},Kc.prototype.notifyOn=function(a){return new Rc(a,this)},Kc.prototype.makeSafe=function(a){return new AnonymousSafeObserver(this._onNext,this._onError,this._onCompleted,a)};var Mc,Nc=Qa.internals.AbstractObserver=function(a){function b(){this.isStopped=!1}return Wb(b,a),b.prototype.next=jb,b.prototype.error=jb,b.prototype.completed=jb,b.prototype.onNext=function(a){!this.isStopped&&this.next(a); },b.prototype.onError=function(a){this.isStopped||(this.isStopped=!0,this.error(a))},b.prototype.onCompleted=function(){this.isStopped||(this.isStopped=!0,this.completed())},b.prototype.dispose=function(){this.isStopped=!0},b.prototype.fail=function(a){return this.isStopped?!1:(this.isStopped=!0,this.error(a),!0)},b}(Kc),Oc=Qa.AnonymousObserver=function(a){function b(b,c,d){a.call(this),this._onNext=b,this._onError=c,this._onCompleted=d}return Wb(b,a),b.prototype.next=function(a){this._onNext(a)},b.prototype.error=function(a){this._onError(a)},b.prototype.completed=function(){this._onCompleted()},b}(Nc),Pc=function(a){function b(b){a.call(this),this._observer=b,this._state=0}Wb(b,a);var c=b.prototype;return c.onNext=function(a){this.checkAccess();var b=$a(this._observer.onNext).call(this._observer,a);this._state=0,b===Za&&e(b.e)},c.onError=function(a){this.checkAccess();var b=$a(this._observer.onError).call(this._observer,a);this._state=2,b===Za&&e(b.e)},c.onCompleted=function(){this.checkAccess();var a=$a(this._observer.onCompleted).call(this._observer);this._state=2,a===Za&&e(a.e)},c.checkAccess=function(){if(1===this._state)throw new Error("Re-entrancy detected");if(2===this._state)throw new Error("Observer completed");0===this._state&&(this._state=1)},b}(Kc),Qc=Qa.internals.ScheduledObserver=function(a){function b(b,c){a.call(this),this.scheduler=b,this.observer=c,this.isAcquired=!1,this.hasFaulted=!1,this.queue=[],this.disposable=new ic}function c(a,b){return function(){a.onNext(b)}}function d(a,b){return function(){a.onError(b)}}function f(a){return function(){a.onCompleted()}}function g(a,b){var c;if(!(a.queue.length>0))return void(a.isAcquired=!1);c=a.queue.shift();var d=$a(c)();return d===Za?(a.queue=[],a.hasFaulted=!0,e(d.e)):void b(a)}return Wb(b,a),b.prototype.next=function(a){this.queue.push(c(this.observer,a))},b.prototype.error=function(a){this.queue.push(d(this.observer,a))},b.prototype.completed=function(){this.queue.push(f(this.observer))},b.prototype.ensureActive=function(){var a=!1;!this.hasFaulted&&this.queue.length>0&&(a=!this.isAcquired,this.isAcquired=!0),a&&this.disposable.setDisposable(this.scheduler.scheduleRecursive(this,g))},b.prototype.dispose=function(){a.prototype.dispose.call(this),this.disposable.dispose()},b}(Nc),Rc=function(a){function b(b,c,d){a.call(this,b,c),this._cancel=d}return Wb(b,a),b.prototype.next=function(b){a.prototype.next.call(this,b),this.ensureActive()},b.prototype.error=function(b){a.prototype.error.call(this,b),this.ensureActive()},b.prototype.completed=function(){a.prototype.completed.call(this),this.ensureActive()},b.prototype.dispose=function(){a.prototype.dispose.call(this),this._cancel&&this._cancel.dispose(),this._cancel=null},b}(Qc),Sc=Qa.Observable=function(){function a(a,b){return function(c){var d=c.onError;return c.onError=function(b){f(b,a),d.call(c,b)},b.call(a,c)}}function b(){if(Qa.config.longStackSupport&&_a){var b=this._subscribe,c=$a(e)(new Error).e;this.stack=c.stack.substring(c.stack.indexOf("\n")+1),this._subscribe=a(this,b)}}return Mc=b.prototype,b.isObservable=function(a){return a&&Ya(a.subscribe)},Mc.subscribe=Mc.forEach=function(a,b,c){return this._subscribe("object"==typeof a?a:Lc(a,b,c))},Mc.subscribeOnNext=function(a,b){return this._subscribe(Lc("undefined"!=typeof b?function(c){a.call(b,c)}:a))},Mc.subscribeOnError=function(a,b){return this._subscribe(Lc(null,"undefined"!=typeof b?function(c){a.call(b,c)}:a))},Mc.subscribeOnCompleted=function(a,b){return this._subscribe(Lc(null,null,"undefined"!=typeof b?function(){a.call(b)}:a))},b}(),Tc=Qa.ObservableBase=function(a){function b(a){return a&&Ya(a.dispose)?a:Ya(a)?cc(a):dc}function c(a,c){var d=c[0],f=c[1],g=$a(f.subscribeCore).call(f,d);g!==Za||d.fail(Za.e)||e(Za.e),d.setDisposable(b(g))}function d(){a.call(this)}return Wb(d,a),d.prototype._subscribe=function(a){var b=new yg(a),d=[b,this];return wc.scheduleRequired()?wc.schedule(d,c):c(null,d),b},d.prototype.subscribeCore=jb,d}(Sc),Uc=Qa.FlatMapObservable=function(a){function b(b,c,d,e){this.resultSelector=Ya(d)?d:null,this.selector=ob(Ya(c)?c:function(){return c},e,3),this.source=b,a.call(this)}function c(a,b,c,d){this.i=0,this.selector=b,this.resultSelector=c,this.source=d,this.o=a,Nc.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new c(a,this.selector,this.resultSelector,this))},Wb(c,Nc),c.prototype._wrapResult=function(a,b,c){return this.resultSelector?a.map(function(a,d){return this.resultSelector(b,a,c,d)},this):a},c.prototype.next=function(a){var b=this.i++,c=$a(this.selector)(a,b,this.source);return c===Za?this.o.onError(c.e):(Xa(c)&&(c=dd(c)),(nb(c)||mb(c))&&(c=Sc.from(c)),void this.o.onNext(this._wrapResult(c,a,b)))},c.prototype.error=function(a){this.o.onError(a)},c.prototype.completed=function(){this.o.onCompleted()},b}(Tc),Vc=Qa.internals.Enumerable=function(){};y.prototype.dispose=function(){this.isDisposed||(this.isDisposed=!0,this._s.isDisposed=!0)};var Wc=function(a){function b(b){this.sources=b,a.call(this)}function c(a,b){if(!a.isDisposed){var c=$a(a.e.next).call(a.e);if(c===Za)return a.o.onError(c.e);if(c.done)return a.o.onCompleted();var e=c.value;Xa(e)&&(e=dd(e));var f=new hc;a.subscription.setDisposable(f),f.setDisposable(e.subscribe(new d(a,b)))}}function d(a,b){this._state=a,this._recurse=b,Nc.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new ic,d={isDisposed:!1,o:a,subscription:b,e:this.sources[kb]()},e=wc.scheduleRecursive(d,c);return new kc([b,e,new y(d)])},Wb(d,Nc),d.prototype.next=function(a){this._state.o.onNext(a)},d.prototype.error=function(a){this._state.o.onError(a)},d.prototype.completed=function(){this._recurse(this._state)},b}(Tc);Vc.prototype.concat=function(){return new Wc(this)};var Xc=function(a){function b(b){this.sources=b,a.call(this)}function c(a,b){if(!a.isDisposed){var c=$a(a.e.next).call(a.e);if(c===Za)return a.o.onError(c.e);if(c.done)return null!==a.lastError?a.o.onError(a.lastError):a.o.onCompleted();var e=c.value;Xa(e)&&(e=dd(e));var f=new hc;a.subscription.setDisposable(f),f.setDisposable(e.subscribe(new d(a,b)))}}function d(a,b){this._state=a,this._recurse=b,Nc.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new ic,d={isDisposed:!1,e:this.sources[kb](),subscription:b,lastError:null,o:a},e=wc.scheduleRecursive(d,c);return new kc([b,e,new y(d)])},Wb(d,Nc),d.prototype.next=function(a){this._state.o.onNext(a)},d.prototype.error=function(a){this._state.lastError=a,this._recurse(this._state)},d.prototype.completed=function(){this._state.o.onCompleted()},b}(Tc);Vc.prototype.catchError=function(){return new Xc(this)};var Yc=function(a){function b(a,b){this.v=a,this.c=null==b?-1:b}function c(a){this.v=a.v,this.l=a.c}return Wb(b,a),b.prototype[kb]=function(){return new c(this)},c.prototype.next=function(){return 0===this.l?lb:(this.l>0&&this.l--,{done:!1,value:this.v})},b}(Vc),Zc=Vc.repeat=function(a,b){return new Yc(a,b)},$c=function(a){function b(a,b,c){this.s=a,this.fn=b?ob(b,c,3):null}function c(a){this.i=-1,this.s=a.s,this.l=this.s.length,this.fn=a.fn}return Wb(b,a),b.prototype[kb]=function(){return new c(this)},c.prototype.next=function(){return++this.id?(a.onNext(b[d]),e(d+1)):a.onCompleted()}}return Wb(b,a),b.prototype.subscribeCore=function(a){return this._scheduler.scheduleRecursive(0,c(a,this._args))},b}(Tc),od=Sc.fromArray=function(a,b){return pc(b)||(b=wc),new nd(a,b)},pd=function(a){function b(b,c,d,e,f){this._initialState=b,this._cndFn=c,this._itrFn=d,this._resFn=e,this._s=f,a.call(this)}function c(a,b){if(a.first)a.first=!1;else if(a.newState=$a(a.self._itrFn)(a.newState),a.newState===Za)return a.o.onError(a.newState.e);var c=$a(a.self._cndFn)(a.newState);if(c===Za)return a.o.onError(c.e);if(c){var d=$a(a.self._resFn)(a.newState);if(d===Za)return a.o.onError(d.e);a.o.onNext(d),b(a)}else a.o.onCompleted()}return Wb(b,a),b.prototype.subscribeCore=function(a){var b={o:a,self:this,first:!0,newState:this._initialState};return this._s.scheduleRecursive(b,c)},b}(Tc);Sc.generate=function(a,b,c,d,e){return pc(e)||(e=wc),new pd(a,b,c,d,e)},Sc.of=function(){for(var a=arguments.length,b=new Array(a),c=0;a>c;c++)b[c]=arguments[c];return new nd(b,wc)},Sc.ofWithScheduler=function(a){for(var b=arguments.length,c=new Array(b-1),d=1;b>d;d++)c[d-1]=arguments[d];return new nd(c,a)},Sc.ofArrayChanges=function(a){if(!Array.isArray(a))throw new TypeError("Array.observe only accepts arrays.");if("function"!=typeof Array.observe&&"function"!=typeof Array.unobserve)throw new TypeError("Array.observe is not supported on your platform");return new xg(function(b){function c(a){for(var c=0,d=a.length;d>c;c++)b.onNext(a[c])}return Array.observe(a,c),function(){Array.unobserve(a,c)}})},Sc.ofObjectChanges=function(a){if(null==a)throw new TypeError("object must not be null or undefined.");if("function"!=typeof Object.observe&&"function"!=typeof Object.unobserve)throw new TypeError("Object.observe is not supported on your platform");return new xg(function(b){function c(a){for(var c=0,d=a.length;d>c;c++)b.onNext(a[c])}return Object.observe(a,c),function(){Object.unobserve(a,c)}})};var qd=function(a){function b(){a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return dc},b}(Tc),rd=new qd,sd=Sc.never=function(){return rd},td=function(a){function b(b,c){this._o=b,this._keys=Object.keys(b),this._scheduler=c,a.call(this)}function c(a,b,c){return function(d,e){if(dd?(c.onNext(a+d),e(d+1)):c.onCompleted()}}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.scheduler.scheduleRecursive(0,c(this.start,this.rangeCount,a))},b}(Tc);Sc.range=function(a,b,c){return pc(c)||(c=wc),new ud(a,b,c)};var vd=function(a){function b(b,c,d){this.value=b,this.repeatCount=null==c?-1:c,this.scheduler=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new I(a,this);return b.run()},b}(Tc);I.prototype.run=function(){function a(a,d){return(-1===a||a>0)&&(b.onNext(c),a>0&&a--),0===a?b.onCompleted():void d(a)}var b=this.observer,c=this.parent.value;return this.parent.scheduler.scheduleRecursive(this.parent.repeatCount,a)},Sc.repeat=function(a,b,c){return pc(c)||(c=wc),new vd(a,b,c)};var wd=function(a){function b(b,c){this._value=b,this._scheduler=c,a.call(this)}function c(a,b){var c=b[0],d=b[1];return d.onNext(c),d.onCompleted(),dc}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=[this._value,a];return this._scheduler===uc?c(null,b):this._scheduler.schedule(b,c)},b}(Tc),xd=(Sc["return"]=Sc.just=function(a,b){return pc(b)||(b=uc),new wd(a,b)},function(a){function b(b,c){this._error=b,this._scheduler=c,a.call(this)}function c(a,b){var c=b[0],d=b[1];return d.onError(c),dc}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=[this._error,a];return this._scheduler===uc?c(null,b):this._scheduler.schedule(b,c)},b}(Tc)),yd=Sc["throw"]=function(a,b){return pc(b)||(b=uc),new xd(a,b)},zd=function(a){function b(b,c){this._resFn=b,this._obsFn=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=dc,c=$a(this._resFn)();if(c===Za)return new jc(yd(c.e).subscribe(a),b);c&&(b=c);var d=$a(this._obsFn)(c);return d===Za?new jc(yd(d.e).subscribe(a),b):new jc(d.subscribe(a),b)},b}(Tc);Sc.using=function(a,b){return new zd(a,b)},Mc.amb=function(a){var b=this;return new xg(function(c){function d(){f||(f=g,j.dispose())}function e(){f||(f=h,i.dispose())}var f,g="L",h="R",i=new hc,j=new hc;Xa(a)&&(a=dd(a));var k=Lc(function(a){d(),f===g&&c.onNext(a)},function(a){d(),f===g&&c.onError(a)},function(){d(),f===g&&c.onCompleted()}),l=Lc(function(a){e(),f===h&&c.onNext(a)},function(a){e(),f===h&&c.onError(a)},function(){e(),f===h&&c.onCompleted()});return i.setDisposable(b.subscribe(k)),j.setDisposable(a.subscribe(l)),new jc(i,j)})},Sc.amb=function(){var a,b=sd();if(Array.isArray(arguments[0]))a=arguments[0];else{var c=arguments.length;a=new Array(a);for(var d=0;c>d;d++)a[d]=arguments[d]}for(var d=0,c=a.length;c>d;d++)b=J(b,a[d]);return b};var Ad=function(a){function b(b,c){this.source=b,this._fn=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new hc,c=new ic;return c.setDisposable(b),b.setDisposable(this.source.subscribe(new Bd(a,c,this._fn))),c},b}(Tc),Bd=function(a){function b(b,c,d){this._o=b,this._s=c,this._fn=d,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._o.onNext(a)},b.prototype.completed=function(){return this._o.onCompleted()},b.prototype.error=function(a){var b=$a(this._fn)(a);if(b===Za)return this._o.onError(b.e);Xa(b)&&(b=dd(b));var c=new hc;this._s.setDisposable(c),c.setDisposable(b.subscribe(this._o))},b}(Nc);Mc["catch"]=function(a){return Ya(a)?new Ad(this,a):Cd([this,a])};var Cd=Sc["catch"]=function(){var a;if(Array.isArray(arguments[0]))a=arguments[0];else{var b=arguments.length;a=new Array(b);for(var c=0;b>c;c++)a[c]=arguments[c]}return _c(a).catchError()};Mc.combineLatest=function(){for(var a=arguments.length,b=new Array(a),c=0;a>c;c++)b[c]=arguments[c];return Array.isArray(b[0])?b[0].unshift(this):b.unshift(this),Fd.apply(this,b)};var Dd=function(a){function b(b,c){this._params=b,this._cb=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){for(var b=this._params.length,c=new Array(b),d={hasValue:u(b,K),hasValueAll:!1,isDone:u(b,K),values:new Array(b)},e=0;b>e;e++){var f=this._params[e],g=new hc;c[e]=g,Xa(f)&&(f=dd(f)),g.setDisposable(f.subscribe(new Ed(a,e,this._cb,d)))}return new kc(c)},b}(Tc),Ed=function(a){function b(b,c,d,e){this._o=b,this._i=c,this._cb=d,this._state=e,a.call(this)}function c(a){return function(b,c){return c!==a}}return Wb(b,a),b.prototype.next=function(a){if(this._state.values[this._i]=a,this._state.hasValue[this._i]=!0,this._state.hasValueAll||(this._state.hasValueAll=this._state.hasValue.every(Sa))){var b=$a(this._cb).apply(null,this._state.values);if(b===Za)return this._o.onError(b.e);this._o.onNext(b)}else this._state.isDone.filter(c(this._i)).every(Sa)&&this._o.onCompleted()},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._state.isDone[this._i]=!0,this._state.isDone.every(Sa)&&this._o.onCompleted()},b}(Nc),Fd=Sc.combineLatest=function(){for(var a=arguments.length,b=new Array(a),c=0;a>c;c++)b[c]=arguments[c];var d=Ya(b[a-1])?b.pop():L;return Array.isArray(b[0])&&(b=b[0]),new Dd(b,d)};Mc.concat=function(){for(var a=[],b=0,c=arguments.length;c>b;b++)a.push(arguments[b]);return a.unshift(this),Id.apply(null,a)};var Gd=function(a){function b(b,c){this._s=b,this._fn=c,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._s.o.onNext(a)},b.prototype.error=function(a){this._s.o.onError(a)},b.prototype.completed=function(){this._s.i++,this._fn(this._s)},b}(Nc),Hd=function(a){function b(b){this._sources=b,a.call(this)}function c(a,b){if(!a.disposable.isDisposed){if(a.i===a.sources.length)return a.o.onCompleted();var c=a.sources[a.i];Xa(c)&&(c=dd(c));var d=new hc;a.subscription.setDisposable(d),d.setDisposable(c.subscribe(new Gd(a,b)))}}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new ic,d=cc(Ra),e={o:a,i:0,subscription:b,disposable:d,sources:this._sources},f=uc.scheduleRecursive(e,c);return new kc([b,d,f])},b}(Tc),Id=Sc.concat=function(){var a;if(Array.isArray(arguments[0]))a=arguments[0];else{a=new Array(arguments.length);for(var b=0,c=arguments.length;c>b;b++)a[b]=arguments[b]}return new Hd(a)};Mc.concatAll=function(){return this.merge(1)};var Jd=function(a){function b(b,c){this.source=b,this.maxConcurrent=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new _b;return b.add(this.source.subscribe(new Kd(a,this.maxConcurrent,b))),b},b}(Tc),Kd=function(a){function b(b,c,d){this.o=b,this.max=c,this.g=d,this.done=!1,this.q=[],this.activeCount=0,a.call(this)}function c(b,c){this.parent=b,this.sad=c,a.call(this)}return Wb(b,a),b.prototype.handleSubscribe=function(a){var b=new hc;this.g.add(b),Xa(a)&&(a=dd(a)),b.setDisposable(a.subscribe(new c(this,b)))},b.prototype.next=function(a){this.activeCount0?this.parent.handleSubscribe(this.parent.q.shift()):(this.parent.activeCount--,this.parent.done&&0===this.parent.activeCount&&this.parent.o.onCompleted())},b}(Nc);Mc.merge=function(a){return"number"!=typeof a?Ld(this,a):new Jd(this,a)};var Ld=Sc.merge=function(){var a,b,c=[],d=arguments.length;if(arguments[0])if(pc(arguments[0]))for(a=arguments[0],b=1;d>b;b++)c.push(arguments[b]);else for(a=uc,b=0;d>b;b++)c.push(arguments[b]);else for(a=uc,b=1;d>b;b++)c.push(arguments[b]);return Array.isArray(c[0])&&(c=c[0]),H(a,c).mergeAll()},Md=function(a){function b(b){this.source=b,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new _b,c=new hc;return b.add(c),c.setDisposable(this.source.subscribe(new Nd(a,b))),b},b}(Tc),Nd=function(a){function b(b,c){this.o=b,this.g=c,this.done=!1,a.call(this)}function c(b,c){this.parent=b,this.sad=c,a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b=new hc;this.g.add(b),Xa(a)&&(a=dd(a)),b.setDisposable(a.subscribe(new c(this,b)))},b.prototype.error=function(a){this.o.onError(a)},b.prototype.completed=function(){this.done=!0,1===this.g.length&&this.o.onCompleted()},Wb(c,a),c.prototype.next=function(a){this.parent.o.onNext(a)},c.prototype.error=function(a){this.parent.o.onError(a)},c.prototype.completed=function(){this.parent.g.remove(this.sad),this.parent.done&&1===this.parent.g.length&&this.parent.o.onCompleted()},b}(Nc);Mc.mergeAll=function(){return new Md(this)};var Od=Qa.CompositeError=function(a){this.innerErrors=a,this.message="This contains multiple errors. Check the innerErrors",Error.call(this)};Od.prototype=Object.create(Error.prototype),Od.prototype.name="CompositeError";var Pd=function(a){function b(b){this.source=b,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new _b,c=new hc,d={isStopped:!1,errors:[],o:a};return b.add(c),c.setDisposable(this.source.subscribe(new Qd(b,d))),b},b}(Tc),Qd=function(a){function b(b,c){this._group=b,this._state=c,a.call(this)}function c(a,b){0===b.length?a.onCompleted():1===b.length?a.onError(b[0]):a.onError(new Od(b))}function d(b,c,d){this._inner=b,this._group=c,this._state=d,a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b=new hc;this._group.add(b),Xa(a)&&(a=dd(a)),b.setDisposable(a.subscribe(new d(b,this._group,this._state)))},b.prototype.error=function(a){this._state.errors.push(a),this._state.isStopped=!0,1===this._group.length&&c(this._state.o,this._state.errors)},b.prototype.completed=function(){this._state.isStopped=!0,1===this._group.length&&c(this._state.o,this._state.errors)},Wb(d,a),d.prototype.next=function(a){this._state.o.onNext(a)},d.prototype.error=function(a){this._state.errors.push(a),this._group.remove(this._inner),this._state.isStopped&&1===this._group.length&&c(this._state.o,this._state.errors)},d.prototype.completed=function(){this._group.remove(this._inner),this._state.isStopped&&1===this._group.length&&c(this._state.o,this._state.errors)},b}(Nc);Sc.mergeDelayError=function(){var a;if(Array.isArray(arguments[0]))a=arguments[0];else{var b=arguments.length;a=new Array(b);for(var c=0;b>c;c++)a[c]=arguments[c]}var d=H(null,a);return new Pd(d)},Mc.onErrorResumeNext=function(a){if(!a)throw new Error("Second observable is required");return Td([this,a])};var Rd=function(a){function b(b){this.sources=b,a.call(this)}function c(a,b){if(a.posc;c++)a[c]=arguments[c]}return new Rd(a)},Ud=function(a){function b(b,c){this._s=b,this._o=Xa(c)?dd(c):c,this._open=!1,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new hc;b.setDisposable(this._s.subscribe(new Vd(a,this))),Xa(this._o)&&(this._o=dd(this._o));var c=new hc;return c.setDisposable(this._o.subscribe(new Wd(a,this,c))),new jc(b,c)},b}(Tc),Vd=function(a){function b(b,c){this._o=b,this._p=c,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._p._open&&this._o.onNext(a)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.onCompleted=function(){this._p._open&&this._o.onCompleted()},b}(Nc),Wd=function(a){function b(b,c,d){this._o=b,this._p=c,this._r=d,a.call(this)}return Wb(b,a),b.prototype.next=function(){this._p._open=!0,this._r.dispose()},b.prototype.error=function(a){this._o.onError(a)},b.prototype.onCompleted=function(){this._r.dispose()},b}(Nc);Mc.skipUntil=function(a){return new Ud(this,a)};var Xd=function(a){function b(b){this.source=b,a.call(this)}function c(a,b){this.o=a,this.inner=b,this.stopped=!1,this.latest=0,this.hasLatest=!1,Nc.call(this)}function d(a,b){this.parent=a,this.id=b,Nc.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new ic,d=this.source.subscribe(new c(a,b));return new jc(d,b)},Wb(c,Nc),c.prototype.next=function(a){var b=new hc,c=++this.latest;this.hasLatest=!0,this.inner.setDisposable(b),Xa(a)&&(a=dd(a)),b.setDisposable(a.subscribe(new d(this,c)))},c.prototype.error=function(a){this.o.onError(a)},c.prototype.completed=function(){this.stopped=!0,!this.hasLatest&&this.o.onCompleted()},Wb(d,Nc),d.prototype.next=function(a){this.parent.latest===this.id&&this.parent.o.onNext(a)},d.prototype.error=function(a){this.parent.latest===this.id&&this.parent.o.onError(a)},d.prototype.completed=function(){this.parent.latest===this.id&&(this.parent.hasLatest=!1,this.parent.stopped&&this.parent.o.onCompleted())},b}(Tc);Mc["switch"]=Mc.switchLatest=function(){return new Xd(this)};var Yd=function(a){function b(b,c){this.source=b,this.other=Xa(c)?dd(c):c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return new jc(this.source.subscribe(a),this.other.subscribe(new Zd(a)))},b}(Tc),Zd=function(a){function b(b){this._o=b,a.call(this)}return Wb(b,a),b.prototype.next=function(){this._o.onCompleted()},b.prototype.error=function(a){this._o.onError(a)},b.prototype.onCompleted=Ra,b}(Nc);Mc.takeUntil=function(a){return new Yd(this,a)};var $d=function(a){function b(b,c,d){this._s=b,this._ss=c,this._cb=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){for(var b=this._ss.length,c={hasValue:u(b,K),hasValueAll:!1,values:new Array(b)},d=this._ss.length,e=new Array(d+1),f=0;d>f;f++){var g=this._ss[f],h=new hc;Xa(g)&&(g=dd(g)),h.setDisposable(g.subscribe(new _d(a,f,c))),e[f]=h}var i=new hc;return i.setDisposable(this._s.subscribe(new ae(a,this._cb,c))),e[d]=i,new kc(e)},b}(Tc),_d=function(a){function b(b,c,d){this._o=b,this._i=c,this._state=d,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._state.values[this._i]=a,this._state.hasValue[this._i]=!0,this._state.hasValueAll=this._state.hasValue.every(Sa)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=Ra,b}(Nc),ae=function(a){function b(b,c,d){this._o=b,this._cb=c,this._state=d,a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b=[a].concat(this._state.values);if(this._state.hasValueAll){var c=$a(this._cb).apply(null,b);return c===Za?this._o.onError(c.e):void this._o.onNext(c)}},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onCompleted()},b}(Nc);Mc.withLatestFrom=function(){if(0===arguments.length)throw new Error("invalid arguments");for(var a=arguments.length,b=new Array(a),c=0;a>c;c++)b[c]=arguments[c];var d=Ya(b[a-1])?b.pop():L;return Array.isArray(b[0])&&(b=b[0]),new $d(this,b,d)};var be=function(a){function b(b,c){this._s=b,this._cb=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){for(var b=this._s.length,c=new Array(b),d=u(b,K),e=u(b,M),f=0;b>f;f++){var g=this._s[f],h=new hc;c[f]=h,Xa(g)&&(g=dd(g)),h.setDisposable(g.subscribe(new ce(a,f,this,e,d)))}return new kc(c)},b}(Tc),ce=function(a){function b(b,c,d,e,f){this._o=b,this._i=c,this._p=d,this._q=e,this._d=f,a.call(this)}function c(a){return a.length>0}function d(a){return a.shift()}function e(a){return function(b,c){return c!==a}}return Wb(b,a),b.prototype.next=function(a){if(this._q[this._i].push(a),this._q.every(c)){var b=this._q.map(d),f=$a(this._p._cb).apply(null,b);if(f===Za)return this._o.onError(f.e);this._o.onNext(f)}else this._d.filter(e(this._i)).every(Sa)&&this._o.onCompleted()},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._d[this._i]=!0,this._d.every(Sa)&&this._o.onCompleted()},b}(Nc);Mc.zip=function(){if(0===arguments.length)throw new Error("invalid arguments");for(var a=arguments.length,b=new Array(a),c=0;a>c;c++)b[c]=arguments[c];var d=Ya(b[a-1])?b.pop():L;Array.isArray(b[0])&&(b=b[0]);var e=this;return b.unshift(e),new be(b,d)},Sc.zip=function(){for(var a=arguments.length,b=new Array(a),c=0;a>c;c++)b[c]=arguments[c];Array.isArray(b[0])&&(b=Ya(b[1])?b[0].concat(b[1]):b[0]);var d=b.shift();return d.zip.apply(d,b)};var de=function(a){function b(b,c){this.sources=b,this._cb=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){for(var b=this.sources,c=b.length,d=new Array(c),e={q:u(c,M),done:u(c,K),cb:this._cb,o:a},f=0;c>f;f++)!function(a){var c=b[a],f=new hc;(nb(c)||mb(c))&&(c=md(c)),d[a]=f,f.setDisposable(c.subscribe(new ee(e,a)))}(f);return new kc(d)},b}(Tc),ee=function(a){function b(b,c){this._s=b,this._i=c,a.call(this)}function c(a){return a.length>0}function d(a){return a.shift()}function e(a){return function(b,c){return c!==a}}return Wb(b,a),b.prototype.next=function(a){if(this._s.q[this._i].push(a),this._s.q.every(c)){var b=this._s.q.map(d),f=$a(this._s.cb).apply(null,b);if(f===Za)return this._s.o.onError(f.e);this._s.o.onNext(f)}else this._s.done.filter(e(this._i)).every(Sa)&&this._s.o.onCompleted()},b.prototype.error=function(a){this._s.o.onError(a)},b.prototype.completed=function(){this._s.done[this._i]=!0,this._s.done.every(Sa)&&this._s.o.onCompleted()},b}(Nc);Mc.zipIterable=function(){if(0===arguments.length)throw new Error("invalid arguments");for(var a=arguments.length,b=new Array(a),c=0;a>c;c++)b[c]=arguments[c];var d=Ya(b[a-1])?b.pop():L,e=this;return b.unshift(e),new de(b,d)},Mc.asObservable=function(){return new xg(N(this),this)},Mc.bufferWithCount=Mc.bufferCount=function(a,b){return"number"!=typeof b&&(b=a),this.windowWithCount(a,b).flatMap(O).filter(P)};var fe=function(a){function b(b){this.source=b,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new ge(a))},b}(Tc),ge=function(a){function b(b){this._o=b,a.call(this)}return Wb(b,a),b.prototype.next=function(a){a.accept(this._o)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onCompleted()},b}(Nc);Mc.dematerialize=function(){return new fe(this)};var he=function(a){function b(b,c,d){this.source=b,this.keyFn=c,this.comparer=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){ return this.source.subscribe(new ie(a,this.keyFn,this.comparer))},b}(Tc),ie=function(a){function b(b,c,d){this.o=b,this.keyFn=c,this.comparer=d,this.hasCurrentKey=!1,this.currentKey=null,a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b,c=a;return Ya(this.keyFn)&&(c=$a(this.keyFn)(a),c===Za)?this.o.onError(c.e):this.hasCurrentKey&&(b=$a(this.comparer)(this.currentKey,c),b===Za)?this.o.onError(b.e):void(this.hasCurrentKey&&b||(this.hasCurrentKey=!0,this.currentKey=c,this.o.onNext(a)))},b.prototype.error=function(a){this.o.onError(a)},b.prototype.completed=function(){this.o.onCompleted()},b}(Nc);Mc.distinctUntilChanged=function(a,b){return b||(b=Ua),new he(this,a,b)};var je=function(a){function b(b,c,d,e){this.source=b,this._oN=c,this._oE=d,this._oC=e,a.call(this)}function c(a,b){this.o=a,this.t=!b._oN||Ya(b._oN)?Lc(b._oN||Ra,b._oE||Ra,b._oC||Ra):b._oN,this.isStopped=!1,Nc.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new c(a,this))},Wb(c,Nc),c.prototype.next=function(a){var b=$a(this.t.onNext).call(this.t,a);b===Za&&this.o.onError(b.e),this.o.onNext(a)},c.prototype.error=function(a){var b=$a(this.t.onError).call(this.t,a);return b===Za?this.o.onError(b.e):void this.o.onError(a)},c.prototype.completed=function(){var a=$a(this.t.onCompleted).call(this.t);return a===Za?this.o.onError(a.e):void this.o.onCompleted()},b}(Tc);Mc["do"]=Mc.tap=Mc.doAction=function(a,b,c){return new je(this,a,b,c)},Mc.doOnNext=Mc.tapOnNext=function(a,b){return this.tap("undefined"!=typeof b?function(c){a.call(b,c)}:a)},Mc.doOnError=Mc.tapOnError=function(a,b){return this.tap(Ra,"undefined"!=typeof b?function(c){a.call(b,c)}:a)},Mc.doOnCompleted=Mc.tapOnCompleted=function(a,b){return this.tap(Ra,null,"undefined"!=typeof b?function(){a.call(b)}:a)};var ke=function(a){function b(b,c,d){this.source=b,this._fn=ob(c,d,0),a.call(this)}function c(a,b){this.isDisposed=!1,this._s=a,this._fn=b}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=$a(this.source.subscribe).call(this.source,a);return b===Za&&(this._fn(),e(b.e)),new c(b,this._fn)},c.prototype.dispose=function(){if(!this.isDisposed){var a=$a(this._s.dispose).call(this._s);this._fn(),a===Za&&e(a.e)}},b}(Tc);Mc["finally"]=function(a,b){return new ke(this,a,b)};var le=function(a){function b(b){this.source=b,a.call(this)}function c(a){this.o=a,this.isStopped=!1}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new c(a))},c.prototype.onNext=Ra,c.prototype.onError=function(a){this.isStopped||(this.isStopped=!0,this.o.onError(a))},c.prototype.onCompleted=function(){this.isStopped||(this.isStopped=!0,this.o.onCompleted())},c.prototype.dispose=function(){this.isStopped=!0},c.prototype.fail=function(a){return this.isStopped?!1:(this.isStopped=!0,this.observer.onError(a),!0)},b}(Tc);Mc.ignoreElements=function(){return new le(this)};var me=function(a){function b(b,c){this.source=b,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new ne(a))},b}(Tc),ne=function(a){function b(b){this._o=b,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._o.onNext(Hc(a))},b.prototype.error=function(a){this._o.onNext(Ic(a)),this._o.onCompleted()},b.prototype.completed=function(){this._o.onNext(Jc()),this._o.onCompleted()},b}(Nc);Mc.materialize=function(){return new me(this)},Mc.repeat=function(a){return Zc(this,a).concat()},Mc.retry=function(a){return Zc(this,a).catchError()};var oe=function(a){function b(a){return{isDisposed:!1,dispose:function(){this.isDisposed||(this.isDisposed=!0,a.isDisposed=!0)}}}function c(b,c){this.source=b,this._notifier=c,a.call(this)}return Wb(c,a),c.prototype.subscribeCore=function(a){var c,d=new Bg,e=new Bg,f=this._notifier(d),g=f.subscribe(e),h=this.source["@@iterator"](),i={isDisposed:!1},j=new ic,k=wc.scheduleRecursive(null,function(b,f){if(!i.isDisposed){var g=h.next();if(g.done)return void(c?a.onError(c):a.onCompleted());var k=g.value;Xa(k)&&(k=dd(k));var l=new hc,m=new hc;j.setDisposable(new jc(m,l)),l.setDisposable(k.subscribe(function(b){a.onNext(b)},function(b){m.setDisposable(e.subscribe(f,function(b){a.onError(b)},function(){a.onCompleted()})),d.onNext(b),l.dispose()},function(){a.onCompleted()}))}});return new kc([g,j,k,b(i)])},c}(Tc);Mc.retryWhen=function(a){return new oe(Q(this),a)};var pe=function(a){function b(a){return{isDisposed:!1,dispose:function(){this.isDisposed||(this.isDisposed=!0,a.isDisposed=!0)}}}function c(b,c){this.source=b,this._notifier=c,a.call(this)}return Wb(c,a),c.prototype.subscribeCore=function(a){var c,d=new Bg,e=new Bg,f=this._notifier(d),g=f.subscribe(e),h=this.source["@@iterator"](),i={isDisposed:!1},j=new ic,k=wc.scheduleRecursive(null,function(b,f){if(!i.isDisposed){var g=h.next();if(g.done)return void(c?a.onError(c):a.onCompleted());var k=g.value;Xa(k)&&(k=dd(k));var l=new hc,m=new hc;j.setDisposable(new jc(m,l)),l.setDisposable(k.subscribe(function(b){a.onNext(b)},function(b){a.onError(b)},function(){m.setDisposable(e.subscribe(f,function(b){a.onError(b)},function(){a.onCompleted()})),d.onNext(null),l.dispose()}))}});return new kc([g,j,k,b(i)])},c}(Tc);Mc.repeatWhen=function(a){return new pe(Q(this),a)};var qe=function(a){function b(b,c,d,e){this.source=b,this.accumulator=c,this.hasSeed=d,this.seed=e,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new re(a,this))},b}(Tc),re=function(a){function b(b,c){this._o=b,this._p=c,this._fn=c.accumulator,this._hs=c.hasSeed,this._s=c.seed,this._ha=!1,this._a=null,this._hv=!1,this._i=0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){return!this._hv&&(this._hv=!0),this._ha?this._a=$a(this._fn)(this._a,a,this._i,this._p):(this._a=this._hs?$a(this._fn)(this._s,a,this._i,this._p):a,this._ha=!0),this._a===Za?this._o.onError(this._a.e):(this._o.onNext(this._a),void this._i++)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){!this._hv&&this._hs&&this._o.onNext(this._s),this._o.onCompleted()},b}(Nc);Mc.scan=function(){var a,b=!1,c=arguments[0];return 2===arguments.length&&(b=!0,a=arguments[1]),new qe(this,c,b,a)};var se=function(a){function b(b,c){this.source=b,this._c=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new te(a,this._c))},b}(Tc),te=function(a){function b(b,c){this._o=b,this._c=c,this._q=[],a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._q.push(a),this._q.length>this._c&&this._o.onNext(this._q.shift())},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onCompleted()},b}(Nc);Mc.skipLast=function(a){if(0>a)throw new gb;return new se(this,a)},Mc.startWith=function(){var a,b=0;arguments.length&&pc(arguments[0])?(a=arguments[0],b=1):a=uc;for(var c=[],d=b,e=arguments.length;e>d;d++)c.push(arguments[d]);return Id.apply(null,[od(c,a),this])};var ue=function(a){function b(b,c){this._o=b,this._c=c,this._q=[],a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._q.push(a),this._q.length>this._c&&this._q.shift()},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){for(;this._q.length>0;)this._o.onNext(this._q.shift());this._o.onCompleted()},b}(Nc);Mc.takeLast=function(a){if(0>a)throw new gb;var b=this;return new xg(function(c){return b.subscribe(new ue(c,a))},b)};var ve=function(a){function b(b,c){this._o=b,this._c=c,this._q=[],a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._q.push(a),this._q.length>this._c&&this._q.shift()},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onNext(this._q),this._o.onCompleted()},b}(Nc);Mc.takeLastBuffer=function(a){if(0>a)throw new gb;var b=this;return new xg(function(c){return b.subscribe(new ve(c,a))},b)},Mc.windowWithCount=Mc.windowCount=function(a,b){var c=this;if(+a||(a=0),Math.abs(a)===1/0&&(a=0),0>=a)throw new gb;if(null==b&&(b=a),+b||(b=0),Math.abs(b)===1/0&&(b=0),0>=b)throw new gb;return new xg(function(d){function e(){var a=new Bg;i.push(a),d.onNext(Yb(a,g))}var f=new hc,g=new lc(f),h=0,i=[];return e(),f.setDisposable(c.subscribe(function(c){for(var d=0,f=i.length;f>d;d++)i[d].onNext(c);var g=h-a+1;g>=0&&g%b===0&&i.shift().onCompleted(),++h%b===0&&e()},function(a){for(;i.length>0;)i.shift().onError(a);d.onError(a)},function(){for(;i.length>0;)i.shift().onCompleted();d.onCompleted()})),g},c)},Mc.selectConcat=Mc.concatMap=function(a,b,c){return Ya(a)&&Ya(b)?this.concatMap(function(c,d){var e=a(c,d);return Xa(e)&&(e=dd(e)),(nb(e)||mb(e))&&(e=md(e)),e.map(function(a,e){return b(c,a,d,e)})}):Ya(a)?R(this,a,c):R(this,function(){return a})},Mc.concatMapObserver=Mc.selectConcatObserver=function(a,b,c,d){var e=this,f=ob(a,d,2),g=ob(b,d,1),h=ob(c,d,0);return new xg(function(a){var b=0;return e.subscribe(function(c){var d;try{d=f(c,b++)}catch(e){return void a.onError(e)}Xa(d)&&(d=dd(d)),a.onNext(d)},function(b){var c;try{c=g(b)}catch(d){return void a.onError(d)}Xa(c)&&(c=dd(c)),a.onNext(c),a.onCompleted()},function(){var b;try{b=h()}catch(c){return void a.onError(c)}Xa(b)&&(b=dd(b)),a.onNext(b),a.onCompleted()})},this).concatAll()};var we=function(a){function b(b,c){this._o=b,this._d=c,this._f=!1,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._f=!0,this._o.onNext(a)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){!this._f&&this._o.onNext(this._d),this._o.onCompleted()},b}(Nc);Mc.defaultIfEmpty=function(b){var c=this;return b===a&&(b=null),new xg(function(a){return c.subscribe(new we(a,b))},c)},T.prototype.push=function(a){var b=-1===S(this.set,a,this.comparer);return b&&this.set.push(a),b};var xe=function(a){function b(b,c,d){this.source=b,this._keyFn=c,this._cmpFn=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new ye(a,this._keyFn,this._cmpFn))},b}(Tc),ye=function(a){function b(b,c,d){this._o=b,this._keyFn=c,this._h=new T(d),a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b=a;return Ya(this._keyFn)&&(b=$a(this._keyFn)(a),b===Za)?this._o.onError(b.e):void(this._h.push(b)&&this._o.onNext(a))},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onCompleted()},b}(Nc);Mc.distinct=function(a,b){return b||(b=Ua),new xe(this,a,b)},Mc.groupBy=function(a,b){return this.groupByUntil(a,b,sd)},Mc.groupByUntil=function(b,c,d){var e=this;return new xg(function(f){var g=new Of,h=new _b,i=new lc(h),j=function(a){return function(b){b.onError(a)}};return h.add(e.subscribe(function(e){var k=$a(b)(e);if(k===Za)return g.forEach(j(k.e)),f.onError(k.e);var l=!1,m=g.get(k);if(m===a&&(m=new Bg,g.set(k,m),l=!0),l){var n=new Ag(k,m,i),o=new Ag(k,m),p=$a(d)(o);if(p===Za)return g.forEach(j(p.e)),f.onError(p.e);f.onNext(n);var q=new hc;h.add(q),q.setDisposable(p.take(1).subscribe(Ra,function(a){g.forEach(j(a)),f.onError(a)},function(){g["delete"](k)&&m.onCompleted(),h.remove(q)}))}var r=e;return Ya(c)&&(r=$a(c)(e),r===Za)?(g.forEach(j(r.e)),f.onError(r.e)):void m.onNext(r)},function(a){g.forEach(j(a)),f.onError(a)},function(){g.forEach(function(a){a.onCompleted()}),f.onCompleted()})),i},e)};var ze=function(a){function b(b,c,d){this.source=b,this.selector=ob(c,d,3),a.call(this)}function c(a,b){return function(c,d,e){return a.call(this,b.selector(c,d,e),d,e)}}function d(a,b,c){this.o=a,this.selector=b,this.source=c,this.i=0,Nc.call(this)}return Wb(b,a),b.prototype.internalMap=function(a,d){return new b(this.source,c(a,this),d)},b.prototype.subscribeCore=function(a){return this.source.subscribe(new d(a,this.selector,this))},Wb(d,Nc),d.prototype.next=function(a){var b=$a(this.selector)(a,this.i++,this.source);return b===Za?this.o.onError(b.e):void this.o.onNext(b)},d.prototype.error=function(a){this.o.onError(a)},d.prototype.completed=function(){this.o.onCompleted()},b}(Tc);Mc.map=Mc.select=function(a,b){var c="function"==typeof a?a:function(){return a};return this instanceof ze?this.internalMap(c,b):new ze(this,c,b)},Mc.pluck=function(){var a=arguments.length,b=new Array(a);if(0===a)throw new Error("List of properties cannot be empty.");for(var c=0;a>c;c++)b[c]=arguments[c];return this.map(U(b,a))},Mc.flatMap=Mc.selectMany=Mc.mergeMap=function(a,b,c){return new Uc(this,a,b,c).mergeAll()},Mc.flatMapObserver=Mc.selectManyObserver=function(a,b,c,d){var e=this;return new xg(function(f){var g=0;return e.subscribe(function(b){var c;try{c=a.call(d,b,g++)}catch(e){return void f.onError(e)}Xa(c)&&(c=dd(c)),f.onNext(c)},function(a){var c;try{c=b.call(d,a)}catch(e){return void f.onError(e)}Xa(c)&&(c=dd(c)),f.onNext(c),f.onCompleted()},function(){var a;try{a=c.call(d)}catch(b){return void f.onError(b)}Xa(a)&&(a=dd(a)),f.onNext(a),f.onCompleted()})},e).mergeAll()},Mc.flatMapLatest=Mc.switchMap=function(a,b,c){return new Uc(this,a,b,c).switchLatest()};var Ae=function(a){function b(b,c){this.source=b,this._count=c,a.call(this)}function c(a,b){this._o=a,this._r=b,Nc.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new c(a,this._count))},Wb(c,Nc),c.prototype.next=function(a){this._r<=0?this._o.onNext(a):this._r--},c.prototype.error=function(a){this._o.onError(a)},c.prototype.completed=function(){this._o.onCompleted()},b}(Tc);Mc.skip=function(a){if(0>a)throw new gb;return new Ae(this,a)};var Be=function(a){function b(b,c){this.source=b,this._fn=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new Ce(a,this))},b}(Tc),Ce=function(a){function b(b,c){this._o=b,this._p=c,this._i=0,this._r=!1,a.call(this)}return Wb(b,a),b.prototype.next=function(a){if(!this._r){var b=$a(this._p._fn)(a,this._i++,this._p);if(b===Za)return this._o.onError(b.e);this._r=!b}this._r&&this._o.onNext(a)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onCompleted()},b}(Nc);Mc.skipWhile=function(a,b){var c=ob(a,b,3);return new Be(this,c)};var De=function(a){function b(b,c){this.source=b,this._count=c,a.call(this)}function c(a,b){this._o=a,this._c=b,this._r=b,Nc.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new c(a,this._count))},Wb(c,Nc),c.prototype.next=function(a){this._r-->0&&(this._o.onNext(a),this._r<=0&&this._o.onCompleted())},c.prototype.error=function(a){this._o.onError(a)},c.prototype.completed=function(){this._o.onCompleted()},b}(Tc);Mc.take=function(a,b){if(0>a)throw new gb;return 0===a?jd(b):new De(this,a)};var Ee=function(a){function b(b,c){this.source=b,this._fn=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new Fe(a,this))},b}(Tc),Fe=function(a){function b(b,c){this._o=b,this._p=c,this._i=0,this._r=!0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){return this._r&&(this._r=$a(this._p._fn)(a,this._i++,this._p),this._r===Za)?this._o.onError(this._r.e):void(this._r?this._o.onNext(a):this._o.onCompleted())},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onCompleted()},b}(Nc);Mc.takeWhile=function(a,b){var c=ob(a,b,3);return new Ee(this,c)};var Ge=function(a){function b(b,c,d){this.source=b,this.predicate=ob(c,d,3),a.call(this)}function c(a,b){return function(c,d,e){return b.predicate(c,d,e)&&a.call(this,c,d,e)}}function d(a,b,c){this.o=a,this.predicate=b,this.source=c,this.i=0,Nc.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new d(a,this.predicate,this))},b.prototype.internalFilter=function(a,d){return new b(this.source,c(a,this),d)},Wb(d,Nc),d.prototype.next=function(a){var b=$a(this.predicate)(a,this.i++,this.source);return b===Za?this.o.onError(b.e):void(b&&this.o.onNext(a))},d.prototype.error=function(a){this.o.onError(a)},d.prototype.completed=function(){this.o.onCompleted()},b}(Tc);Mc.filter=Mc.where=function(a,b){return this instanceof Ge?this.internalFilter(a,b):new Ge(this,a,b)};var He=function(a){function b(b,c,d){this.source=b,this._k=c,this._c=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new Ie(a,this._k,this._c))},b}(Tc),Ie=function(a){function b(b,c,d){this._o=b,this._k=c,this._c=d,this._v=null,this._hv=!1,this._l=[],a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b=$a(this._k)(a);if(b===Za)return this._o.onError(b.e);var c=0;if(this._hv){if(c=$a(this._c)(b,this._v),c===Za)return this._o.onError(c.e)}else this._hv=!0,this._v=b;c>0&&(this._v=b,this._l=[]),c>=0&&this._l.push(a)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onNext(this._l),this._o.onCompleted()},b}(Nc),Je=function(a){function b(b,c,d,e){this.source=b,this.accumulator=c,this.hasSeed=d,this.seed=e,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new Ke(a,this))},b}(Tc),Ke=function(a){function b(b,c){this._o=b,this._p=c,this._fn=c.accumulator,this._hs=c.hasSeed,this._s=c.seed,this._ha=!1,this._a=null,this._hv=!1,this._i=0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){return!this._hv&&(this._hv=!0),this._ha?this._a=$a(this._fn)(this._a,a,this._i,this._p):(this._a=this._hs?$a(this._fn)(this._s,a,this._i,this._p):a,this._ha=!0),this._a===Za?this._o.onError(this._a.e):void this._i++},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._hv&&this._o.onNext(this._a),!this._hv&&this._hs&&this._o.onNext(this._s),!this._hv&&!this._hs&&this._o.onError(new eb),this._o.onCompleted()},b}(Nc);Mc.reduce=function(){var a,b=!1,c=arguments[0];return 2===arguments.length&&(b=!0,a=arguments[1]),new Je(this,c,b,a)};var Le=function(a){function b(b,c){this.source=b,this._fn=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new Me(a,this._fn,this.source))},b}(Tc),Me=function(a){function b(b,c,d){this._o=b,this._fn=c,this._s=d,this._i=0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b=$a(this._fn)(a,this._i++,this._s);return b===Za?this._o.onError(b.e):void(Boolean(b)&&(this._o.onNext(!0),this._o.onCompleted()))},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onNext(!1),this._o.onCompleted()},b}(Nc);Mc.some=function(a,b){var c=ob(a,b,3);return new Le(this,c)};var Ne=function(a){function b(b){this.source=b,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new Oe(a))},b}(Tc),Oe=function(a){function b(b){this._o=b,a.call(this)}return Wb(b,a),b.prototype.next=function(){this._o.onNext(!1),this._o.onCompleted()},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onNext(!0),this._o.onCompleted()},b}(Nc);Mc.isEmpty=function(){return new Ne(this)};var Pe=function(a){function b(b,c){this.source=b,this._fn=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new Qe(a,this._fn,this.source))},b}(Tc),Qe=function(a){function b(b,c,d){this._o=b,this._fn=c,this._s=d,this._i=0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b=$a(this._fn)(a,this._i++,this._s);return b===Za?this._o.onError(b.e):void(Boolean(b)||(this._o.onNext(!1),this._o.onCompleted()))},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onNext(!0),this._o.onCompleted()},b}(Nc);Mc.every=function(a,b){var c=ob(a,b,3);return new Pe(this,c)};var Re=function(a){function b(b,c,d){var e=+d||0;Math.abs(e)===1/0&&(e=0),this.source=b,this._elem=c,this._n=e,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this._n<0?(a.onNext(!1),a.onCompleted(),dc):this.source.subscribe(new Se(a,this._elem,this._n))},b}(Tc),Se=function(a){function b(b,c,d){this._o=b,this._elem=c,this._n=d,this._i=0,a.call(this)}function c(a,b){return 0===a&&0===b||a===b||isNaN(a)&&isNaN(b)}return Wb(b,a),b.prototype.next=function(a){this._i++>=this._n&&c(a,this._elem)&&(this._o.onNext(!0),this._o.onCompleted())},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onNext(!1),this._o.onCompleted()},b}(Nc);Mc.includes=function(a,b){return new Re(this,a,b)};var Te=function(a){function b(b,c){this.source=b,this._fn=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new Ue(a,this._fn,this.source))},b}(Tc),Ue=function(a){function b(b,c,d){this._o=b,this._fn=c,this._s=d,this._i=0,this._c=0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){if(this._fn){var b=$a(this._fn)(a,this._i++,this._s);if(b===Za)return this._o.onError(b.e);Boolean(b)&&this._c++}else this._c++},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onNext(this._c),this._o.onCompleted()},b}(Nc);Mc.count=function(a,b){var c=ob(a,b,3);return new Te(this,c)};var Ve=function(a){function b(b,c,d){this.source=b,this._e=c,this._n=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this._n<0?(a.onNext(-1),a.onCompleted(),dc):this.source.subscribe(new We(a,this._e,this._n))},b}(Tc),We=function(a){function b(b,c,d){this._o=b,this._e=c,this._n=d,this._i=0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._i>=this._n&&a===this._e&&(this._o.onNext(this._i),this._o.onCompleted()),this._i++},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onNext(-1),this._o.onCompleted()},b}(Nc);Mc.indexOf=function(a,b){var c=+b||0;return Math.abs(c)===1/0&&(c=0),new Ve(this,a,c)};var Xe=function(a){function b(b,c){this.source=b,this._fn=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new Ye(a,this._fn,this.source))},b}(Tc),Ye=function(a){function b(b,c,d){this._o=b,this._fn=c,this._s=d,this._i=0,this._c=0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){if(this._fn){var b=$a(this._fn)(a,this._i++,this._s);if(b===Za)return this._o.onError(b.e);this._c+=b}else this._c+=a},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onNext(this._c),this._o.onCompleted()},b}(Nc);Mc.sum=function(a,b){var c=ob(a,b,3);return new Xe(this,c)},Mc.minBy=function(a,b){return b||(b=Va),new He(this,a,function(a,c){return-1*b(a,c)})},Mc.min=function(a){return this.minBy(Sa,a).map(V)},Mc.maxBy=function(a,b){return b||(b=Va),new He(this,a,b)},Mc.max=function(a){return this.maxBy(Sa,a).map(V)};var Ze=function(a){function b(b,c){this.source=b,this._fn=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new $e(a,this._fn,this.source))},b}(Tc),$e=function(a){function b(b,c,d){this._o=b,this._fn=c,this._s=d,this._c=0,this._t=0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){if(this._fn){var b=$a(this._fn)(a,this._c++,this._s);if(b===Za)return this._o.onError(b.e);this._t+=b}else this._c++,this._t+=a},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){return 0===this._c?this._o.onError(new eb):(this._o.onNext(this._t/this._c),void this._o.onCompleted())},b}(Nc);Mc.average=function(a,b){var c,d=this;return Ya(a)&&(c=ob(a,b,3)),new Ze(d,c)},Mc.sequenceEqual=function(a,b){var c=this;return b||(b=Ua),new xg(function(d){var e=!1,f=!1,g=[],h=[],i=c.subscribe(function(a){if(h.length>0){var c=h.shift(),e=$a(b)(c,a);if(e===Za)return d.onError(e.e);e||(d.onNext(!1),d.onCompleted())}else f?(d.onNext(!1),d.onCompleted()):g.push(a)},function(a){d.onError(a)},function(){e=!0,0===g.length&&(h.length>0?(d.onNext(!1),d.onCompleted()):f&&(d.onNext(!0),d.onCompleted()))});(nb(a)||mb(a))&&(a=md(a)),Xa(a)&&(a=dd(a));var j=a.subscribe(function(a){if(g.length>0){var c=g.shift(),f=$a(b)(c,a);if(f===Za)return d.onError(f.e);f||(d.onNext(!1),d.onCompleted())}else e?(d.onNext(!1),d.onCompleted()):h.push(a)},function(a){d.onError(a)},function(){f=!0,0===h.length&&(g.length>0?(d.onNext(!1),d.onCompleted()):e&&(d.onNext(!0),d.onCompleted()))});return new jc(i,j)},c)};var _e=function(a){function b(b,c,d){this.source=b,this._i=c,this._d=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new af(a,this._i,this._d))},b}(Tc),af=function(b){function c(a,c,d){this._o=a,this._i=c,this._d=d,b.call(this)}return Wb(c,b),c.prototype.next=function(a){0===this._i--&&(this._o.onNext(a),this._o.onCompleted())},c.prototype.error=function(a){this._o.onError(a)},c.prototype.completed=function(){this._d===a?this._o.onError(new gb):(this._o.onNext(this._d),this._o.onCompleted())},c}(Nc);Mc.elementAt=function(a,b){if(0>a)throw new gb;return new _e(this,a,b)};var bf=function(b){function c(a,c,d){this._o=a,this._obj=c,this._s=d,this._i=0,this._hv=!1,this._v=null,b.call(this)}return Wb(c,b),c.prototype.next=function(a){var b=!1;if(this._obj.predicate){var c=$a(this._obj.predicate)(a,this._i++,this._s);if(c===Za)return this._o.onError(c.e);Boolean(c)&&(b=!0)}else this._obj.predicate||(b=!0);if(b){if(this._hv)return this._o.onError(new Error("Sequence contains more than one matching element"));this._hv=!0,this._v=a}},c.prototype.error=function(a){this._o.onError(a)},c.prototype.completed=function(){this._hv?(this._o.onNext(this._v),this._o.onCompleted()):this._obj.defaultValue===a?this._o.onError(new eb):(this._o.onNext(this._obj.defaultValue),this._o.onCompleted())},c}(Nc);Mc.single=function(a,b){var c={},d=this;if(c="object"==typeof arguments[0]?arguments[0]:{predicate:arguments[0],thisArg:arguments[1],defaultValue:arguments[2]},Ya(c.predicate)){var e=c.predicate;c.predicate=ob(e,c.thisArg,3)}return new xg(function(a){return d.subscribe(new bf(a,c,d))},d)};var cf=function(a){function b(b,c){this.source=b,this._obj=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new df(a,this._obj,this.source))},b}(Tc),df=function(b){function c(a,c,d){this._o=a,this._obj=c,this._s=d,this._i=0,b.call(this)}return Wb(c,b),c.prototype.next=function(a){if(this._obj.predicate){var b=$a(this._obj.predicate)(a,this._i++,this._s);if(b===Za)return this._o.onError(b.e);Boolean(b)&&(this._o.onNext(a),this._o.onCompleted())}else this._obj.predicate||(this._o.onNext(a),this._o.onCompleted())},c.prototype.error=function(a){this._o.onError(a)},c.prototype.completed=function(){this._obj.defaultValue===a?this._o.onError(new eb):(this._o.onNext(this._obj.defaultValue),this._o.onCompleted())},c}(Nc);Mc.first=function(){var a={};if(a="object"==typeof arguments[0]?arguments[0]:{predicate:arguments[0],thisArg:arguments[1],defaultValue:arguments[2]},Ya(a.predicate)){var b=a.predicate;a.predicate=ob(b,a.thisArg,3)}return new cf(this,a)};var ef=function(a){function b(b,c){this.source=b,this._obj=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new ff(a,this._obj,this.source))},b}(Tc),ff=function(b){function c(a,c,d){this._o=a,this._obj=c,this._s=d,this._i=0,this._hv=!1,this._v=null,b.call(this)}return Wb(c,b),c.prototype.next=function(a){var b=!1;if(this._obj.predicate){var c=$a(this._obj.predicate)(a,this._i++,this._s);if(c===Za)return this._o.onError(c.e);Boolean(c)&&(b=!0)}else this._obj.predicate||(b=!0);b&&(this._hv=!0,this._v=a)},c.prototype.error=function(a){this._o.onError(a)},c.prototype.completed=function(){this._hv?(this._o.onNext(this._v),this._o.onCompleted()):this._obj.defaultValue===a?this._o.onError(new eb):(this._o.onNext(this._obj.defaultValue),this._o.onCompleted())},c}(Nc);Mc.last=function(){var a={};if(a="object"==typeof arguments[0]?arguments[0]:{predicate:arguments[0],thisArg:arguments[1],defaultValue:arguments[2]},Ya(a.predicate)){var b=a.predicate;a.predicate=ob(b,a.thisArg,3)}return new ef(this,a)};var gf=function(a){function b(b,c,d,e){this._o=b,this._s=c,this._cb=d,this._y=e,this._i=0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b=$a(this._cb)(a,this._i,this._s);return b===Za?this._o.onError(b.e):void(b?(this._o.onNext(this._y?this._i:a),this._o.onCompleted()):this._i++)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._y&&this._o.onNext(-1),this._o.onCompleted()},b}(Nc);Mc.find=function(a,b){return W(this,a,b,!1)},Mc.findIndex=function(a,b){return W(this,a,b,!0)};var hf=function(a){function b(b){this.source=b,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new jf(a))},b}(Tc),jf=function(a){function b(b){this._o=b,this._s=new Pa.Set,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._s.add(a)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onNext(this._s),this._o.onCompleted()},b}(Nc);Mc.toSet=function(){if("undefined"==typeof Pa.Set)throw new TypeError;return new hf(this)};var kf=function(a){function b(b,c,d){this.source=b,this._k=c,this._e=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new lf(a,this._k,this._e))},b}(Tc),lf=function(a){function b(b,c,d){this._o=b,this._k=c,this._e=d,this._m=new Pa.Map,a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b=$a(this._k)(a);if(b===Za)return this._o.onError(b.e);var c=a;return this._e&&(c=$a(this._e)(a),c===Za)?this._o.onError(c.e):void this._m.set(b,c)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onNext(this._m),this._o.onCompleted()},b}(Nc);Mc.toMap=function(a,b){if("undefined"==typeof Pa.Map)throw new TypeError;return new kf(this,a,b)};var mf=function(a){function b(b,c,d){this.source=b,this._b=c,this._e=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new nf(a,this._b,this._e))},b}(Tc),nf=function(a){function b(b,c,d){this._o=b,this._b=c,this._e=d,this._i=0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._i>=this._b&&(this._e===this._i?this._o.onCompleted():this._o.onNext(a)),this._i++},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onCompleted()},b}(Nc);Mc.slice=function(a,b){var c=a||0;if(0>c)throw new Qa.ArgumentOutOfRangeError;if("number"==typeof b&&c>b)throw new Qa.ArgumentOutOfRangeError;return new mf(this,c,b)};var of=function(a){function b(b,c,d){this.source=b,this._e=c,this._n=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this._n<0?(a.onNext(-1),a.onCompleted(),dc):this.source.subscribe(new pf(a,this._e,this._n))},b}(Tc),pf=function(a){function b(b,c,d){this._o=b,this._e=c,this._n=d,this._v=0,this._hv=!1,this._i=0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._i>=this._n&&a===this._e&&(this._hv=!0,this._v=this._i),this._i++},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._hv?this._o.onNext(this._v):this._o.onNext(-1),this._o.onCompleted()},b}(Nc);Mc.lastIndexOf=function(a,b){var c=+b||0;return Math.abs(c)===1/0&&(c=0),new of(this,a,c)},Sc.wrap=function(a){function b(){return Sc.spawn.call(this,a.apply(this,arguments))}return b.__generatorFunction__=a,b};var qf=Sc.spawn=function(){for(var a=arguments[0],b=this,c=[],d=1,e=arguments.length;e>d;d++)c.push(arguments[d]);return new xg(function(d){function e(b){var c=$a(a.next).call(a,b);return c===Za?d.onError(c.e):void g(c)}function f(b){var c=$a(a.next).call(a,b);return c===Za?d.onError(c.e):void g(c)}function g(a){if(a.done)return d.onNext(a.value),void d.onCompleted();var c=X.call(b,a.value),g=null,i=!1;Sc.isObservable(c)?h.add(c.subscribe(function(a){i=!0,g=a},f,function(){i&&e(g)})):f(new TypeError("type not supported"))}var h=new _b;return Ya(a)&&(a=a.apply(b,c)),a&&Ya(a.next)?(e(),h):(d.onNext(a),d.onCompleted())})};Sc.start=function(a,b,c){return rf(a,b,c)()};var rf=Sc.toAsync=function(a,b,c){return pc(c)||(c=Bc),function(){var d=arguments,e=new Cg;return c.schedule(null,function(){var c;try{c=a.apply(b,d)}catch(f){return void e.onError(f)}e.onNext(c),e.onCompleted()}),e.asObservable()}};Sc.fromCallback=function(a,b,c){return function(){"undefined"==typeof b&&(b=this); for(var d=arguments.length,e=new Array(d),f=0;d>f;f++)e[f]=arguments[f];return ca(a,b,c,e)}},Sc.fromNodeCallback=function(a,b,c){return function(){"undefined"==typeof b&&(b=this);for(var d=arguments.length,e=new Array(d),f=0;d>f;f++)e[f]=arguments[f];return ea(a,b,c,e)}},ha.prototype.dispose=function(){this.isDisposed||(this._e.removeEventListener(this._n,this._fn,!1),this.isDisposed=!0)},Qa.config.useNativeEvents=!1;var sf=function(a){function b(b,c,d){this._el=b,this._n=c,this._fn=d,a.call(this)}function c(a,b){return function(){var c=arguments[0];return Ya(b)&&(c=$a(b).apply(null,arguments),c===Za)?a.onError(c.e):void a.onNext(c)}}return Wb(b,a),b.prototype.subscribeCore=function(a){return ia(this._el,this._n,c(a,this._fn))},b}(Tc);Sc.fromEvent=function(a,b,c){return a.addListener?uf(function(c){a.addListener(b,c)},function(c){a.removeListener(b,c)},c):Qa.config.useNativeEvents||"function"!=typeof a.on||"function"!=typeof a.off?new sf(a,b,c).publish().refCount():uf(function(c){a.on(b,c)},function(c){a.off(b,c)},c)};var tf=function(a){function b(b,c,d){this._add=b,this._del=c,this._fn=d,a.call(this)}function c(a,b){return function(){var c=arguments[0];return Ya(b)&&(c=$a(b).apply(null,arguments),c===Za)?a.onError(c.e):void a.onNext(c)}}function d(a,b,c){this._del=a,this._fn=b,this._ret=c,this.isDisposed=!1}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=c(a,this._fn),e=this._add(b);return new d(this._del,b,e)},d.prototype.dispose=function(){this.isDisposed||(Ya(this._del)&&this._del(this._fn,this._ret),this.isDisposed=!0)},b}(Tc),uf=Sc.fromEventPattern=function(a,b,c){return new tf(a,b,c).publish().refCount()};Sc.startAsync=function(a){var b=$a(a)();return b===Za?yd(b.e):dd(b)};var vf=function(a){function b(b,c){this.source=b,this.controller=new Bg,this.paused=!0,c&&c.subscribe?this.pauser=this.controller.merge(c):this.pauser=this.controller,a.call(this)}return Wb(b,a),b.prototype._subscribe=function(a){var b=this.source.publish(),c=b.subscribe(a),d=dc,e=this.pauser.startWith(!this.paused).distinctUntilChanged().subscribe(function(a){a?d=b.connect():(d.dispose(),d=dc)});return new kc([c,d,e])},b.prototype.pause=function(){this.paused=!0,this.controller.onNext(!1)},b.prototype.resume=function(){this.paused=!1,this.controller.onNext(!0)},b}(Sc);Mc.pausable=function(a){return new vf(this,a)};var wf=function(b){function c(a,c){this.source=a,this.controller=new Bg,this.paused=!0,c&&c.subscribe?this.pauser=this.controller.merge(c):this.pauser=this.controller,b.call(this)}return Wb(c,b),c.prototype._subscribe=function(b){function c(){for(;e.length>0;)b.onNext(e.shift())}var d,e=[],f=ja(this.source,this.pauser.startWith(!this.paused).distinctUntilChanged(),function(a,b){return{data:a,shouldFire:b}}).subscribe(function(f){d!==a&&f.shouldFire!==d?(d=f.shouldFire,f.shouldFire&&c()):(d=f.shouldFire,f.shouldFire?b.onNext(f.data):e.push(f.data))},function(a){c(),b.onError(a)},function(){c(),b.onCompleted()});return f},c.prototype.pause=function(){this.paused=!0,this.controller.onNext(!1)},c.prototype.resume=function(){this.paused=!1,this.controller.onNext(!0)},c}(Sc);Mc.pausableBuffered=function(a){return new wf(this,a)};var xf=function(a){function b(b,c,d){a.call(this),this.subject=new yf(c,d),this.source=b.multicast(this.subject).refCount()}return Wb(b,a),b.prototype._subscribe=function(a){return this.source.subscribe(a)},b.prototype.request=function(a){return this.subject.request(null==a?-1:a)},b}(Sc),yf=function(a){function b(b,c){null==b&&(b=!0),a.call(this),this.subject=new Bg,this.enableQueue=b,this.queue=b?[]:null,this.requestedCount=0,this.requestedDisposable=null,this.error=null,this.hasFailed=!1,this.hasCompleted=!1,this.scheduler=c||wc}return Wb(b,a),Xb(b.prototype,Kc,{_subscribe:function(a){return this.subject.subscribe(a)},onCompleted:function(){this.hasCompleted=!0,this.enableQueue&&0!==this.queue.length?this.queue.push(Dc.createOnCompleted()):(this.subject.onCompleted(),this.disposeCurrentRequest())},onError:function(a){this.hasFailed=!0,this.error=a,this.enableQueue&&0!==this.queue.length?this.queue.push(Dc.createOnError(a)):(this.subject.onError(a),this.disposeCurrentRequest())},onNext:function(a){this.requestedCount<=0?this.enableQueue&&this.queue.push(Dc.createOnNext(a)):(0===this.requestedCount--&&this.disposeCurrentRequest(),this.subject.onNext(a))},_processRequest:function(a){if(this.enableQueue)for(;this.queue.length>0&&(a>0||"N"!==this.queue[0].kind);){var b=this.queue.shift();b.accept(this.subject),"N"===b.kind?a--:(this.disposeCurrentRequest(),this.queue=[])}return a},request:function(a){this.disposeCurrentRequest();var b=this;return this.requestedDisposable=this.scheduler.schedule(a,function(a,c){var d=b._processRequest(c),e=b.hasCompleted||b.hasFailed;return!e&&d>0?(b.requestedCount=d,cc(function(){b.requestedCount=0})):void 0}),this.requestedDisposable},disposeCurrentRequest:function(){this.requestedDisposable&&(this.requestedDisposable.dispose(),this.requestedDisposable=null)}}),b}(Sc);Mc.controlled=function(a,b){return a&&pc(a)&&(b=a,a=!0),null==a&&(a=!0),new xf(this,a,b)};var zf=function(a){function b(b){a.call(this),this.source=b}function c(a,b){return b.source.request(1)}Wb(b,a),b.prototype._subscribe=function(a){return this.subscription=this.source.subscribe(new d(a,this,this.subscription)),new jc(this.subscription,Bc.schedule(this,c))};var d=function(a){function b(b,c,d){a.call(this),this.observer=b,this.observable=c,this.cancel=d,this.scheduleDisposable=null}function c(a,b){return b.observable.source.request(1)}return Wb(b,a),b.prototype.completed=function(){this.observer.onCompleted(),this.dispose()},b.prototype.error=function(a){this.observer.onError(a),this.dispose()},b.prototype.next=function(a){this.observer.onNext(a),this.scheduleDisposable=Bc.schedule(this,c)},b.dispose=function(){this.observer=null,this.cancel&&(this.cancel.dispose(),this.cancel=null),this.scheduleDisposable&&(this.scheduleDisposable.dispose(),this.scheduleDisposable=null),a.prototype.dispose.call(this)},b}(Nc);return b}(Sc);xf.prototype.stopAndWait=function(){return new zf(this)};var Af=function(a){function b(b,c){a.call(this),this.source=b,this.windowSize=c}function c(a,b){return b.source.request(b.windowSize)}Wb(b,a),b.prototype._subscribe=function(a){return this.subscription=this.source.subscribe(new d(a,this,this.subscription)),new jc(this.subscription,Bc.schedule(this,c))};var d=function(a){function b(b,c,d){this.observer=b,this.observable=c,this.cancel=d,this.received=0,this.scheduleDisposable=null,a.call(this)}function c(a,b){return b.observable.source.request(b.observable.windowSize)}return Wb(b,a),b.prototype.completed=function(){this.observer.onCompleted(),this.dispose()},b.prototype.error=function(a){this.observer.onError(a),this.dispose()},b.prototype.next=function(a){this.observer.onNext(a),this.received=++this.received%this.observable.windowSize,0===this.received&&(this.scheduleDisposable=Bc.schedule(this,c))},b.prototype.dispose=function(){this.observer=null,this.cancel&&(this.cancel.dispose(),this.cancel=null),this.scheduleDisposable&&(this.scheduleDisposable.dispose(),this.scheduleDisposable=null),a.prototype.dispose.call(this)},b}(Nc);return b}(Sc);xf.prototype.windowed=function(a){return new Af(this,a)},Mc.pipe=function(a){function b(){c.resume()}var c=this.pausableBuffered();return a.addListener("drain",b),c.subscribe(function(b){!a.write(b)&&c.pause()},function(b){a.emit("error",b)},function(){!a._isStdio&&a.end(),a.removeListener("drain",b)}),c.resume(),a};var Bf=function(a){function b(b,c,d){this.source=b,this._fn1=c,this._fn2=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=this.source.multicast(this._fn1());return new jc(this._fn2(b).subscribe(a),b.connect())},b}(Tc);Mc.multicast=function(a,b){return Ya(a)?new Bf(this,a,b):new Ef(this,a)},Mc.publish=function(a){return a&&Ya(a)?this.multicast(function(){return new Bg},a):this.multicast(new Bg)},Mc.share=function(){return this.publish().refCount()},Mc.publishLast=function(a){return a&&Ya(a)?this.multicast(function(){return new Cg},a):this.multicast(new Cg)},Mc.publishValue=function(a,b){return 2===arguments.length?this.multicast(function(){return new Dg(b)},a):this.multicast(new Dg(a))},Mc.shareValue=function(a){return this.publishValue(a).refCount()},Mc.replay=function(a,b,c,d){return a&&Ya(a)?this.multicast(function(){return new Eg(b,c,d)},a):this.multicast(new Eg(b,c,d))},Mc.shareReplay=function(a,b,c){return this.replay(null,a,b,c).refCount()};var Cf=function(a,b){this._s=a,this._o=b};Cf.prototype.dispose=function(){if(!this._s.isDisposed&&null!==this._o){var a=this._s.observers.indexOf(this._o);this._s.observers.splice(a,1),this._o=null}};var Df=function(a){function b(b){this.source=b,this._count=0,this._connectableSubscription=null,a.call(this)}function c(a,b){this._p=a,this._s=b,this.isDisposed=!1}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=this.source.subscribe(a);return 1===++this._count&&(this._connectableSubscription=this.source.connect()),new c(this,b)},c.prototype.dispose=function(){this.isDisposed||(this.isDisposed=!0,this._s.dispose(),0===--this._p._count&&this._p._connectableSubscription.dispose())},b}(Tc),Ef=Qa.ConnectableObservable=function(a){function b(b,c){this.source=b,this._connection=null,this._source=b.asObservable(),this._subject=c,a.call(this)}function c(a,b){this._p=a,this._s=b}return Wb(b,a),c.prototype.dispose=function(){this._s&&(this._s.dispose(),this._s=null,this._p._connection=null)},b.prototype.connect=function(){if(!this._connection){if(this._subject.isStopped)return dc;var a=this._source.subscribe(this._subject);this._connection=new c(this,a)}return this._connection},b.prototype._subscribe=function(a){return this._subject.subscribe(a)},b.prototype.refCount=function(){return new Df(this)},b}(Sc);Mc.singleInstance=function(){function a(){return d||(d=!0,b=c["finally"](function(){d=!1}).publish().refCount()),b}var b,c=this,d=!1;return new xg(function(b){return a().subscribe(b)})},Mc.join=function(a,b,c,d){var e=this;return new xg(function(f){var g=new _b,h=!1,i=!1,j=0,k=0,l=new Of,m=new Of,n=function(a){f.onError(a)};return g.add(e.subscribe(function(a){var c=j++,e=new hc;l.set(c,a),g.add(e);var i=$a(b)(a);return i===Za?f.onError(i.e):(e.setDisposable(i.take(1).subscribe(Ra,n,function(){l["delete"](c)&&0===l.size&&h&&f.onCompleted(),g.remove(e)})),void m.forEach(function(b){var c=$a(d)(a,b);return c===Za?f.onError(c.e):void f.onNext(c)}))},n,function(){h=!0,(i||0===l.size)&&f.onCompleted()})),g.add(a.subscribe(function(a){var b=k++,e=new hc;m.set(b,a),g.add(e);var h=$a(c)(a);return h===Za?f.onError(h.e):(e.setDisposable(h.take(1).subscribe(Ra,n,function(){m["delete"](b)&&0===m.size&&i&&f.onCompleted(),g.remove(e)})),void l.forEach(function(b){var c=$a(d)(b,a);return c===Za?f.onError(c.e):void f.onNext(c)}))},n,function(){i=!0,(h||0===m.size)&&f.onCompleted()})),g},e)},Mc.groupJoin=function(a,b,c,d){var e=this;return new xg(function(f){function g(a){}var h=new _b,i=new lc(h),j=new Of,k=new Of,l=0,m=0,g=function(a){return function(b){b.onError(a)}};return h.add(e.subscribe(function(a){var c=new Bg,e=l++;j.set(e,c);var m=$a(d)(a,Yb(c,i));if(m===Za)return j.forEach(g(m.e)),f.onError(m.e);f.onNext(m),k.forEach(function(a){c.onNext(a)});var n=new hc;h.add(n);var o=$a(b)(a);return o===Za?(j.forEach(g(o.e)),f.onError(o.e)):void n.setDisposable(o.take(1).subscribe(Ra,function(a){j.forEach(g(a)),f.onError(a)},function(){j["delete"](e)&&c.onCompleted(),h.remove(n)}))},function(a){j.forEach(g(a)),f.onError(a)},function(){f.onCompleted()})),h.add(a.subscribe(function(a){var b=m++;k.set(b,a);var d=new hc;h.add(d);var e=$a(c)(a);return e===Za?(j.forEach(g(e.e)),f.onError(e.e)):(d.setDisposable(e.take(1).subscribe(Ra,function(a){j.forEach(g(a)),f.onError(a)},function(){k["delete"](b),h.remove(d)})),void j.forEach(function(b){b.onNext(a)}))},function(a){j.forEach(g(a)),f.onError(a)})),i},e)},Mc.buffer=function(){return this.window.apply(this,arguments).flatMap(O)},Mc.window=function(a,b){return 1===arguments.length&&"function"!=typeof arguments[0]?la.call(this,a):"function"==typeof a?ma.call(this,a):ka.call(this,a,b)};var Ff=function(a){function b(b){this.source=b,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new Gf(a))},b}(Tc),Gf=function(a){function b(b){this._o=b,this._p=null,this._hp=!1,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._hp?this._o.onNext([this._p,a]):this._hp=!0,this._p=a},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onCompleted()},b}(Nc);Mc.pairwise=function(){return new Ff(this)},Mc.partition=function(a,b){var c=ob(a,b,3);return[this.filter(a,b),this.filter(function(a,b,d){return!c(a,b,d)})]};var Hf=function(a){function b(a,b){this.c=a,this.s=b}return Wb(b,a),b.prototype[kb]=function(){var a=this;return{next:function(){return a.c()?{done:!1,value:a.s}:{done:!0,value:void 0}}}},b}(Vc);Mc.letBind=Mc.let=function(a){return a(this)},Sc["if"]=function(a,b,c){return gd(function(){return c||(c=jd()),Xa(b)&&(b=dd(b)),Xa(c)&&(c=dd(c)),"function"==typeof c.now&&(c=jd(c)),a()?b:c})},Sc["for"]=Sc.forIn=function(a,b,c){return _c(a,b,c).concat()};var If=Sc["while"]=Sc.whileDo=function(a,b){return Xa(b)&&(b=dd(b)),na(a,b).concat()};Mc.doWhile=function(a){return Id([this,If(a,this)])},Sc["case"]=function(a,b,c){return gd(function(){Xa(c)&&(c=dd(c)),c||(c=jd()),pc(c)&&(c=jd(c));var d=b[a()];return Xa(d)&&(d=dd(d)),d||c})};var Jf=function(a){function b(b,c,d){this.source=b,this._fn=c,this._scheduler=d,a.call(this)}function c(a,b){var c,d=a[0],e=a[1];if(!(d.q.length>0))return void(d.isAcquired=!1);c=d.q.shift();var f=new hc;d.d.add(f),f.setDisposable(c.subscribe(new Kf(d,e,f))),b([d,e])}return Wb(b,a),b.prototype._ensureActive=function(a){var b=!1;a.q.length>0&&(b=!a.isAcquired,a.isAcquired=!0),b&&a.m.setDisposable(this._scheduler.scheduleRecursive([a,this],c))},b.prototype.subscribeCore=function(a){var b=new ic,c=new _b(b),d={q:[],m:b,d:c,activeCount:0,isAcquired:!1,o:a};return d.q.push(this.source),d.activeCount++,this._ensureActive(d),c},b}(Tc),Kf=function(a){function b(b,c,d){this._s=b,this._p=c,this._m1=d,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._s.o.onNext(a);var b=$a(this._p._fn)(a);return b===Za?this._s.o.onError(b.e):(this._s.q.push(b),this._s.activeCount++,void this._p._ensureActive(this._s))},b.prototype.error=function(a){this._s.o.onError(a)},b.prototype.completed=function(){this._s.d.remove(this._m1),this._s.activeCount--,0===this._s.activeCount&&this._s.o.onCompleted()},b}(Nc);Mc.expand=function(a,b){return pc(b)||(b=wc),new Jf(this,a,b)};var Lf=function(a){function b(b,c){this._sources=b,this._cb=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){if(0===this._sources.length)return a.onCompleted(),dc;for(var b=this._sources.length,c={finished:!1,hasResults:new Array(b),hasCompleted:new Array(b),results:new Array(b)},d=new _b,e=0,f=this._sources.length;f>e;e++){var g=this._sources[e];Xa(g)&&(g=dd(g)),d.add(g.subscribe(new Mf(a,c,e,this._cb,d)))}return d},b}(Tc),Mf=function(a){function b(b,c,d,e,f){this._o=b,this._s=c,this._i=d,this._cb=e,this._subs=f,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._s.finished||(this._s.hasResults[this._i]=!0,this._s.results[this._i]=a)},b.prototype.error=function(a){this._s.finished=!0,this._o.onError(a),this._subs.dispose()},b.prototype.completed=function(){if(!this._s.finished){if(!this._s.hasResults[this._i])return this._o.onCompleted();this._s.hasCompleted[this._i]=!0;for(var a=0;ac;c++)b[c]=arguments[c];var d=Ya(b[a-1])?b.pop():L;return Array.isArray(b[0])&&(b=b[0]),new Lf(b,d)},Mc.forkJoin=function(){for(var a=arguments.length,b=new Array(a),c=0;a>c;c++)b[c]=arguments[c];return Array.isArray(b[0])?b[0].unshift(this):b.unshift(this),Sc.forkJoin.apply(null,b)},Mc.manySelect=Mc.extend=function(a,b){pc(b)||(b=Qa.Scheduler.immediate);var c=this;return gd(function(){var d;return c.map(function(a){var b=new Nf(a);return d&&d.onNext(a),d=b,b}).tap(Ra,function(a){d&&d.onError(a)},function(){d&&d.onCompleted()}).observeOn(b).map(a)},c)};var Nf=function(a){function b(b){a.call(this),this.head=b,this.tail=new Cg}return Wb(b,a),Xb(b.prototype,Kc,{_subscribe:function(a){var b=new _b;return b.add(wc.schedule(this,function(c,d){a.onNext(d.head),b.add(d.tail.mergeAll().subscribe(a))})),b},onCompleted:function(){this.onNext(Sc.empty())},onError:function(a){this.onNext(Sc["throw"](a))},onNext:function(a){this.tail.onNext(a),this.tail.onCompleted()}}),b}(Sc),Of=Pa.Map||function(){function b(){this.size=0,this._values=[],this._keys=[]}return b.prototype["delete"]=function(a){var b=this._keys.indexOf(a);return-1===b?!1:(this._values.splice(b,1),this._keys.splice(b,1),this.size--,!0)},b.prototype.get=function(b){var c=this._keys.indexOf(b);return-1===c?a:this._values[c]},b.prototype.set=function(a,b){var c=this._keys.indexOf(a);return-1===c?(this._keys.push(a),this._values.push(b),this.size++):this._values[c]=b,this},b.prototype.forEach=function(a,b){for(var c=0;cf;f++)d.push(sa(a,this.expression.patterns[f],e));var h=new ta(d,ra(this,b),function(){for(var a=0,b=d.length;b>a;a++)d[a].removeActivePlan(h);c(h)});for(f=0,g=d.length;g>f;f++)d[f].addActivePlan(h);return h},ta.prototype.dequeue=function(){this.joinObservers.forEach(function(a){a.queue.shift()})},ta.prototype.match=function(){var a,b,c=!0;for(a=0,b=this.joinObserverArray.length;b>a;a++)if(0===this.joinObserverArray[a].queue.length){c=!1;break}if(c){var d=[],e=!1;for(a=0,b=this.joinObserverArray.length;b>a;a++)d.push(this.joinObserverArray[a].queue[0]),"C"===this.joinObserverArray[a].queue[0].kind&&(e=!0);if(e)this.onCompleted();else{this.dequeue();var f=[];for(a=0,b=d.length;ac;c++)b[c].match()}},c.error=Ra,c.completed=Ra,c.addActivePlan=function(a){this.activePlans.push(a)},c.subscribe=function(){this.subscription.setDisposable(this.source.materialize().subscribe(this))},c.removeActivePlan=function(a){this.activePlans.splice(this.activePlans.indexOf(a),1),0===this.activePlans.length&&this.dispose()},c.dispose=function(){a.prototype.dispose.call(this),this.isDisposed||(this.isDisposed=!0,this.subscription.dispose())},b}(Nc);Mc.and=function(a){return new oa([this,a])},Mc.thenDo=function(a){return new oa([this]).thenDo(a)},Sc.when=function(){var a,b=arguments.length;if(Array.isArray(arguments[0]))a=arguments[0];else{a=new Array(b);for(var c=0;b>c;c++)a[c]=arguments[c]}return new xg(function(b){var c=[],d=new Of,e=Lc(function(a){b.onNext(a)},function(a){d.forEach(function(b){b.onError(a)}),b.onError(a)},function(a){b.onCompleted()});try{for(var f=0,g=a.length;g>f;f++)c.push(a[f].activate(d,e,function(a){var d=c.indexOf(a);c.splice(d,1),0===c.length&&b.onCompleted()}))}catch(h){return yd(h).subscribe(b)}var i=new _b;return d.forEach(function(a){a.subscribe(),i.add(a)}),i})};var Qf=function(a){function b(b,c){this._dt=b,this._s=c,a.call(this)}function c(a,b){b.onNext(0),b.onCompleted()}return Wb(b,a),b.prototype.subscribeCore=function(a){return this._s.scheduleFuture(a,this._dt,c)},b}(Tc),Rf=Sc.interval=function(a,b){return wa(a,a,pc(b)?b:Bc)};Sc.timer=function(b,c,d){var e;return pc(d)||(d=Bc),null!=c&&"number"==typeof c?e=c:pc(c)&&(d=c),(b instanceof Date||"number"==typeof b)&&e===a?ua(b,d):b instanceof Date&&e!==a?va(b,c,d):wa(b,e,d)};Mc.delay=function(){var a=arguments[0];if("number"==typeof a||a instanceof Date){var b=a,c=arguments[1];return pc(c)||(c=Bc),b instanceof Date?ya(this,b,c):xa(this,b,c)}if(Sc.isObservable(a)||Ya(a))return za(this,a,arguments[1]);throw new Error("Invalid arguments")};var Sf=function(a){function b(b,c,d){pc(d)||(d=Bc),this.source=b,this._dt=c,this._s=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new ic;return new jc(this.source.subscribe(new Tf(a,this._dt,this._s,b)),b)},b}(Tc),Tf=function(a){function b(b,c,d,e){this._o=b,this._d=c,this._scheduler=d,this._c=e,this._v=null,this._hv=!1,this._id=0,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._hv=!0,this._v=a;var b=++this._id,c=new hc;this._c.setDisposable(c),c.setDisposable(this._scheduler.scheduleFuture(this,this._d,function(c,d){d._hv&&d._id===b&&d._o.onNext(a),d._hv=!1}))},b.prototype.error=function(a){this._c.dispose(),this._o.onError(a),this._hv=!1,this._id++},b.prototype.completed=function(){this._c.dispose(),this._hv&&this._o.onNext(this._v),this._o.onCompleted(),this._hv=!1,this._id++},b}(Nc);Mc.debounce=function(){if(Ya(arguments[0]))return Aa(this,arguments[0]);if("number"==typeof arguments[0])return new Sf(this,arguments[0],arguments[1]);throw new Error("Invalid arguments")},Mc.windowWithTime=Mc.windowTime=function(a,b,c){var d,e=this;return null==b&&(d=a),pc(c)||(c=Bc),"number"==typeof b?d=b:pc(b)&&(d=a,c=b),new xg(function(b){function f(){var a=new hc,e=!1,g=!1;l.setDisposable(a),j===i?(e=!0,g=!0):i>j?e=!0:g=!0;var n=e?j:i,o=n-m;m=n,e&&(j+=d),g&&(i+=d),a.setDisposable(c.scheduleFuture(null,o,function(){if(g){var a=new Bg;k.push(a),b.onNext(Yb(a,h))}e&&k.shift().onCompleted(),f()}))}var g,h,i=d,j=a,k=[],l=new ic,m=0;return g=new _b(l),h=new lc(g),k.push(new Bg),b.onNext(Yb(k[0],h)),f(),g.add(e.subscribe(function(a){for(var b=0,c=k.length;c>b;b++)k[b].onNext(a)},function(a){for(var c=0,d=k.length;d>c;c++)k[c].onError(a);b.onError(a)},function(){for(var a=0,c=k.length;c>a;a++)k[a].onCompleted();b.onCompleted()})),h},e)},Mc.windowWithTimeOrCount=Mc.windowTimeOrCount=function(a,b,c){var d=this;return pc(c)||(c=Bc),new xg(function(e){function f(b){var d=new hc;g.setDisposable(d),d.setDisposable(c.scheduleFuture(null,a,function(){if(b===k){j=0;var a=++k;l.onCompleted(),l=new Bg,e.onNext(Yb(l,i)),f(a)}}))}var g=new ic,h=new _b(g),i=new lc(h),j=0,k=0,l=new Bg;return e.onNext(Yb(l,i)),f(0),h.add(d.subscribe(function(a){var c=0,d=!1;l.onNext(a),++j===b&&(d=!0,j=0,c=++k,l.onCompleted(),l=new Bg,e.onNext(Yb(l,i))),d&&f(c)},function(a){l.onError(a),e.onError(a)},function(){l.onCompleted(),e.onCompleted()})),i},d)},Mc.bufferWithTime=Mc.bufferTime=function(a,b,c){return this.windowWithTime(a,b,c).flatMap(O)},Mc.bufferWithTimeOrCount=Mc.bufferTimeOrCount=function(a,b,c){return this.windowWithTimeOrCount(a,b,c).flatMap(O)};var Uf=function(a){function b(b,c){this.source=b,this._s=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new Vf(a,this._s))},b}(Tc),Vf=function(a){function b(b,c){this._o=b,this._s=c,this._l=c.now(),a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b=this._s.now(),c=b-this._l;this._l=b,this._o.onNext({value:a,interval:c})},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onCompleted()},b}(Nc);Mc.timeInterval=function(a){return pc(a)||(a=Bc),new Uf(this,a)};var Wf=function(a){function b(b,c){this.source=b,this._s=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new Xf(a,this._s))},b}(Tc),Xf=function(a){function b(b,c){this._o=b,this._s=c,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._o.onNext({value:a,timestamp:this._s.now()})},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onCompleted()},b}(Nc);Mc.timestamp=function(a){return pc(a)||(a=Bc),new Wf(this,a)};var Yf=function(a){function b(b,c){this.source=b,this._sampler=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b={o:a,atEnd:!1,value:null,hasValue:!1,sourceSubscription:new hc};return b.sourceSubscription.setDisposable(this.source.subscribe(new $f(b))),new jc(b.sourceSubscription,this._sampler.subscribe(new Zf(b)))},b}(Tc),Zf=function(a){function b(b){this._s=b,a.call(this)}return Wb(b,a),b.prototype._handleMessage=function(){this._s.hasValue&&(this._s.hasValue=!1,this._s.o.onNext(this._s.value)),this._s.atEnd&&this._s.o.onCompleted()},b.prototype.next=function(){this._handleMessage()},b.prototype.error=function(a){this._s.onError(a)},b.prototype.completed=function(){this._handleMessage()},b}(Nc),$f=function(a){function b(b){this._s=b,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._s.hasValue=!0,this._s.value=a},b.prototype.error=function(a){this._s.o.onError(a)},b.prototype.completed=function(){this._s.atEnd=!0,this._s.sourceSubscription.dispose()},b}(Nc);Mc.sample=function(a,b){return pc(b)||(b=Bc),"number"==typeof a?new Yf(this,Rf(a,b)):new Yf(this,a)};var _f=Qa.TimeoutError=function(a){this.message=a||"Timeout has occurred",this.name="TimeoutError",Error.call(this)};_f.prototype=Object.create(Error.prototype),Mc.timeout=function(){var a=arguments[0];if(a instanceof Date||"number"==typeof a)return Ca(this,a,arguments[1],arguments[2]);if(Sc.isObservable(a)||Ya(a))return Ba(this,a,arguments[1],arguments[2]);throw new Error("Invalid arguments")};var ag=function(a){function b(b,c,d,e,f,g){this._state=b,this._cndFn=c,this._itrFn=d,this._resFn=e,this._timeFn=f,this._s=g,a.call(this)}function c(a,b){if(a.hasResult&&a.o.onNext(a.result),a.first)a.first=!1;else if(a.newState=$a(a.self._itrFn)(a.newState),a.newState===Za)return a.o.onError(a.newState.e);if(a.hasResult=$a(a.self._cndFn)(a.newState),a.hasResult===Za)return a.o.onError(a.hasResult.e);if(a.hasResult){if(a.result=$a(a.self._resFn)(a.newState),a.result===Za)return a.o.onError(a.result.e);var c=$a(a.self._timeFn)(a.newState);if(c===Za)return a.o.onError(c.e);b(a,c)}else a.o.onCompleted()}return Wb(b,a),b.prototype.subscribeCore=function(a){var b={o:a,self:this,newState:this._state,first:!0,hasResult:!1};return this._s.scheduleRecursiveFuture(b,new Date(this._s.now()),c)},b}(Tc);Sc.generateWithAbsoluteTime=function(a,b,c,d,e,f){return pc(f)||(f=Bc),new ag(a,b,c,d,e,f)};var bg=function(a){function b(b,c,d,e,f,g){this._state=b,this._cndFn=c,this._itrFn=d,this._resFn=e,this._timeFn=f,this._s=g,a.call(this)}function c(a,b){if(a.hasResult&&a.o.onNext(a.result),a.first)a.first=!1;else if(a.newState=$a(a.self._itrFn)(a.newState),a.newState===Za)return a.o.onError(a.newState.e);if(a.hasResult=$a(a.self._cndFn)(a.newState),a.hasResult===Za)return a.o.onError(a.hasResult.e);if(a.hasResult){if(a.result=$a(a.self._resFn)(a.newState),a.result===Za)return a.o.onError(a.result.e);var c=$a(a.self._timeFn)(a.newState);if(c===Za)return a.o.onError(c.e);b(a,c)}else a.o.onCompleted()}return Wb(b,a),b.prototype.subscribeCore=function(a){var b={o:a,self:this,newState:this._state,first:!0,hasResult:!1};return this._s.scheduleRecursiveFuture(b,0,c)},b}(Tc);Sc.generateWithRelativeTime=function(a,b,c,d,e,f){return pc(f)||(f=Bc),new bg(a,b,c,d,e,f)};var cg=function(a){function b(b,c,d){this.source=b,this._dt=c,this._s=d,a.call(this)}function c(a,b){var c=b[0],d=b[1],e=b[2];e.setDisposable(c.subscribe(d))}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new ic;return b.setDisposable(this._s.scheduleFuture([this.source,a,b],this._dt,c)),b},b}(Tc);Mc.delaySubscription=function(a,b){return pc(b)||(b=Bc),new cg(this,a,b)};var dg=function(a){function b(b,c,d){this.source=b,this._d=c,this._s=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new eg(a,this))},b}(Tc),eg=function(a){function b(b,c){this._o=b,this._s=c._s,this._d=c._d,this._q=[],a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b=this._s.now();for(this._q.push({interval:b,value:a});this._q.length>0&&b-this._q[0].interval>=this._d;)this._o.onNext(this._q.shift().value)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){for(var a=this._s.now();this._q.length>0&&a-this._q[0].interval>=this._d;)this._o.onNext(this._q.shift().value);this._o.onCompleted()},b}(Nc);Mc.skipLastWithTime=function(a,b){return pc(b)||(b=Bc),new dg(this,a,b)};var fg=function(a){function b(b,c,d){this.source=b,this._d=c,this._s=d,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return this.source.subscribe(new gg(a,this._d,this._s))},b}(Tc),gg=function(a){function b(b,c,d){this._o=b,this._d=c,this._s=d,this._q=[],a.call(this)}return Wb(b,a),b.prototype.next=function(a){var b=this._s.now();for(this._q.push({interval:b,value:a});this._q.length>0&&b-this._q[0].interval>=this._d;)this._q.shift()},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){for(var a=this._s.now();this._q.length>0;){var b=this._q.shift();a-b.interval<=this._d&&this._o.onNext(b.value)}this._o.onCompleted()},b}(Nc);Mc.takeLastWithTime=function(a,b){return pc(b)||(b=Bc),new fg(this,a,b)},Mc.takeLastBufferWithTime=function(a,b){var c=this;return pc(b)||(b=Bc),new xg(function(d){var e=[];return c.subscribe(function(c){var d=b.now();for(e.push({interval:d,value:c});e.length>0&&d-e[0].interval>=a;)e.shift()},function(a){d.onError(a)},function(){for(var c=b.now(),f=[];e.length>0;){var g=e.shift();c-g.interval<=a&&f.push(g.value)}d.onNext(f),d.onCompleted()})},c)};var hg=function(a){function b(b,c,d){this.source=b,this._d=c,this._s=d,a.call(this)}function c(a,b){b.onCompleted()}return Wb(b,a),b.prototype.subscribeCore=function(a){return new jc(this._s.scheduleFuture(a,this._d,c),this.source.subscribe(a))},b}(Tc);Mc.takeWithTime=function(a,b){return pc(b)||(b=Bc),new hg(this,a,b)};var ig=function(a){function b(b,c,d){this.source=b,this._d=c,this._s=d,this._open=!1,a.call(this)}function c(a,b){b._open=!0}return Wb(b,a),b.prototype.subscribeCore=function(a){return new jc(this._s.scheduleFuture(this,this._d,c),this.source.subscribe(new jg(a,this)))},b}(Tc),jg=function(a){function b(b,c){this._o=b,this._p=c,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._p._open&&this._o.onNext(a)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onCompleted()},b}(Nc);Mc.skipWithTime=function(a,b){return pc(b)||(b=Bc),new ig(this,a,b)};var kg=function(a){function b(b,c,d){this.source=b,this._st=c,this._s=d,a.call(this)}function c(a,b){b._open=!0}return Wb(b,a),b.prototype.subscribeCore=function(a){return this._open=!1,new jc(this._s.scheduleFuture(this,this._st,c),this.source.subscribe(new lg(a,this)))},b}(Tc),lg=function(a){function b(b,c){this._o=b,this._p=c,a.call(this)}return Wb(b,a),b.prototype.next=function(a){this._p._open&&this._o.onNext(a)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._o.onCompleted()},b}(Nc);Mc.skipUntilWithTime=function(a,b){return pc(b)||(b=Bc),new kg(this,a,b)},Mc.takeUntilWithTime=function(a,b){pc(b)||(b=Bc);var c=this;return new xg(function(d){return new jc(b.scheduleFuture(d,a,function(a,b){b.onCompleted()}),c.subscribe(d))},c)},Mc.throttle=function(a,b){pc(b)||(b=Bc);var c=+a||0;if(0>=c)throw new RangeError("windowDuration cannot be less or equal zero.");var d=this;return new xg(function(a){var e=0;return d.subscribe(function(d){var f=b.now();(0===e||f-e>=c)&&(e=f,a.onNext(d))},function(b){a.onError(b)},function(){a.onCompleted()})},d)};var mg=function(a){function b(b,c){this._o=b,this._xform=c,a.call(this)}return Wb(b,a),b.prototype.next=function(a){ var b=$a(this._xform["@@transducer/step"]).call(this._xform,this._o,a);b===Za&&this._o.onError(b.e)},b.prototype.error=function(a){this._o.onError(a)},b.prototype.completed=function(){this._xform["@@transducer/result"](this._o)},b}(Nc);Mc.transduce=function(a){var b=this;return new xg(function(c){var d=a(Da(c));return b.subscribe(new mg(c,d))},b)};var ng=function(a){function b(b){this.source=b,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){var b=new hc,c=new _b,d={hasCurrent:!1,isStopped:!1,o:a,g:c};return c.add(b),b.setDisposable(this.source.subscribe(new og(d))),c},b}(Tc),og=function(a){function b(b){this._s=b,a.call(this)}function c(b,c){this._s=b,this._i=c,a.call(this)}return Wb(b,a),b.prototype.next=function(a){if(!this._s.hasCurrent){this._s.hasCurrent=!0,Xa(a)&&(a=dd(a));var b=new hc;this._s.g.add(b),b.setDisposable(a.subscribe(new c(this._s,b)))}},b.prototype.error=function(a){this._s.o.onError(a)},b.prototype.completed=function(){this._s.isStopped=!0,!this._s.hasCurrent&&1===this._s.g.length&&this._s.o.onCompleted()},Wb(c,a),c.prototype.next=function(a){this._s.o.onNext(a)},c.prototype.error=function(a){this._s.o.onError(a)},c.prototype.completed=function(){this._s.g.remove(this._i),this._s.hasCurrent=!1,this._s.isStopped&&1===this._s.g.length&&this._s.o.onCompleted()},b}(Nc);Mc.switchFirst=function(){return new ng(this)},Mc.flatMapFirst=Mc.exhaustMap=function(a,b,c){return new Uc(this,a,b,c).switchFirst()},Mc.flatMapWithMaxConcurrent=Mc.flatMapMaxConcurrent=function(a,b,c,d){return new Uc(this,b,c,d).merge(a)};var pg=Qa.VirtualTimeScheduler=function(a){function b(b,c){this.clock=b,this.comparer=c,this.isEnabled=!1,this.queue=new Zb(1024),a.call(this)}Wb(b,a);var c=b.prototype;return c.now=function(){return this.toAbsoluteTime(this.clock)},c.schedule=function(a,b){return this.scheduleAbsolute(a,this.clock,b)},c.scheduleFuture=function(a,b,c){var d=b instanceof Date?this.toRelativeTime(b-this.now()):this.toRelativeTime(b);return this.scheduleRelative(a,d,c)},c.add=jb,c.toAbsoluteTime=jb,c.toRelativeTime=jb,c.schedulePeriodic=function(a,b,c){var d=new sc(this,a,b,c);return d.start()},c.scheduleRelative=function(a,b,c){var d=this.add(this.clock,b);return this.scheduleAbsolute(a,d,c)},c.start=function(){if(!this.isEnabled){this.isEnabled=!0;do{var a=this.getNext();null!==a?(this.comparer(a.dueTime,this.clock)>0&&(this.clock=a.dueTime),a.invoke()):this.isEnabled=!1}while(this.isEnabled)}},c.stop=function(){this.isEnabled=!1},c.advanceTo=function(a){var b=this.comparer(this.clock,a);if(this.comparer(this.clock,a)>0)throw new gb;if(0!==b&&!this.isEnabled){this.isEnabled=!0;do{var c=this.getNext();null!==c&&this.comparer(c.dueTime,a)<=0?(this.comparer(c.dueTime,this.clock)>0&&(this.clock=c.dueTime),c.invoke()):this.isEnabled=!1}while(this.isEnabled);this.clock=a}},c.advanceBy=function(a){var b=this.add(this.clock,a),c=this.comparer(this.clock,b);if(c>0)throw new gb;0!==c&&this.advanceTo(b)},c.sleep=function(a){var b=this.add(this.clock,a);if(this.comparer(this.clock,b)>=0)throw new gb;this.clock=b},c.getNext=function(){for(;this.queue.length>0;){var a=this.queue.peek();if(!a.isCancelled())return a;this.queue.dequeue()}return null},c.scheduleAbsolute=function(a,b,c){function d(a,b){return e.queue.remove(f),c(a,b)}var e=this,f=new mc(this,a,d,b,this.comparer);return this.queue.enqueue(f),f.disposable},b}(nc);Qa.HistoricalScheduler=function(a){function b(b,c){var d=null==b?0:b,e=c||Va;a.call(this,d,e)}Wb(b,a);var c=b.prototype;return c.add=function(a,b){return a+b},c.toAbsoluteTime=function(a){return new Date(a).getTime()},c.toRelativeTime=function(a){return a},b}(Qa.VirtualTimeScheduler),Ea.prototype.equals=function(a){return a===this?!0:null==a?!1:"N"!==a.kind?!1:this.predicate(a.value)},Fa.prototype.equals=function(a){return a===this?!0:null==a?!1:"E"!==a.kind?!1:this.predicate(a.error)};var qg=Qa.ReactiveTest={created:100,subscribed:200,disposed:1e3,onNext:function(a,b){return"function"==typeof b?new rg(a,new Ea(b)):new rg(a,Dc.createOnNext(b))},onError:function(a,b){return"function"==typeof b?new rg(a,new Fa(b)):new rg(a,Dc.createOnError(b))},onCompleted:function(a){return new rg(a,Dc.createOnCompleted())},subscribe:function(a,b){return new sg(a,b)}},rg=Qa.Recorded=function(a,b,c){this.time=a,this.value=b,this.comparer=c||Ua};rg.prototype.equals=function(a){return this.time===a.time&&this.comparer(this.value,a.value)},rg.prototype.toString=function(){return this.value.toString()+"@"+this.time};var sg=Qa.Subscription=function(a,b){this.subscribe=a,this.unsubscribe=b||Number.MAX_VALUE};sg.prototype.equals=function(a){return this.subscribe===a.subscribe&&this.unsubscribe===a.unsubscribe},sg.prototype.toString=function(){return"("+this.subscribe+", "+(this.unsubscribe===Number.MAX_VALUE?"Infinite":this.unsubscribe)+")"};var tg=Qa.MockDisposable=function(a){this.scheduler=a,this.disposes=[],this.disposes.push(this.scheduler.clock)};tg.prototype.dispose=function(){this.disposes.push(this.scheduler.clock)};var ug=function(a){function b(b){a.call(this),this.scheduler=b,this.messages=[]}Wb(b,a);var c=b.prototype;return c.onNext=function(a){this.messages.push(new rg(this.scheduler.clock,Dc.createOnNext(a)))},c.onError=function(a){this.messages.push(new rg(this.scheduler.clock,Dc.createOnError(a)))},c.onCompleted=function(){this.messages.push(new rg(this.scheduler.clock,Dc.createOnCompleted()))},b}(Kc);Ga.prototype.then=function(b,c){var d=this;this.subscriptions.push(new sg(this.scheduler.clock));var e,f=this.subscriptions.length-1,g=Qa.Observer.create(function(c){var h=b(c);if(h&&"function"==typeof h.then)e=h;else{var i=d.scheduler.clock;e=new Ga(d.scheduler,[Qa.ReactiveTest.onNext(i,a),Qa.ReactiveTest.onCompleted(i)])}var j=d.observers.indexOf(g);d.observers.splice(j,1),d.subscriptions[f]=new sg(d.subscriptions[f].subscribe,d.scheduler.clock)},function(a){c(a);var b=d.observers.indexOf(g);d.observers.splice(b,1),d.subscriptions[f]=new sg(d.subscriptions[f].subscribe,d.scheduler.clock)});return this.observers.push(g),e||new Ga(this.scheduler,this.messages)};var vg=function(a){function b(b,c){a.call(this);var d,e,f=this;this.scheduler=b,this.messages=c,this.subscriptions=[],this.observers=[];for(var g=0,h=this.messages.length;h>g;g++)d=this.messages[g],e=d.value,function(a){b.scheduleAbsolute(null,d.time,function(){for(var b=f.observers.slice(0),c=0,d=b.length;d>c;c++)a.accept(b[c]);return dc})}(e)}return Wb(b,a),b.prototype._subscribe=function(a){var b=this;this.observers.push(a),this.subscriptions.push(new sg(this.scheduler.clock));var c=this.subscriptions.length-1;return cc(function(){var d=b.observers.indexOf(a);b.observers.splice(d,1),b.subscriptions[c]=new sg(b.subscriptions[c].subscribe,b.scheduler.clock)})},b}(Sc),wg=function(a){function b(b,c){a.call(this),this.scheduler=b,this.messages=c,this.subscriptions=[]}return Wb(b,a),b.prototype._subscribe=function(a){var b,c,d=this;this.subscriptions.push(new sg(this.scheduler.clock));for(var e=this.subscriptions.length-1,f=new _b,g=0,h=this.messages.length;h>g;g++)b=this.messages[g],c=b.value,function(c){f.add(d.scheduler.scheduleRelative(null,b.time,function(){return c.accept(a),dc}))}(c);return cc(function(){d.subscriptions[e]=new sg(d.subscriptions[e].subscribe,d.scheduler.clock),f.dispose()})},b}(Sc);Qa.TestScheduler=function(a){function b(a,b){return a>b?1:b>a?-1:0}function c(){a.call(this,0,b)}return Wb(c,a),c.prototype.scheduleAbsolute=function(b,c,d){return c<=this.clock&&(c=this.clock+1),a.prototype.scheduleAbsolute.call(this,b,c,d)},c.prototype.add=function(a,b){return a+b},c.prototype.toAbsoluteTime=function(a){return new Date(a).getTime()},c.prototype.toRelativeTime=function(a){return a},c.prototype.startScheduler=function(a,b){b||(b={}),null==b.created&&(b.created=qg.created),null==b.subscribed&&(b.subscribed=qg.subscribed),null==b.disposed&&(b.disposed=qg.disposed);var c,d,e=this.createObserver();return this.scheduleAbsolute(null,b.created,function(){return c=a(),dc}),this.scheduleAbsolute(null,b.subscribed,function(){return d=c.subscribe(e),dc}),this.scheduleAbsolute(null,b.disposed,function(){return d.dispose(),dc}),this.start(),e},c.prototype.createHotObservable=function(){var a,b=arguments.length;if(Array.isArray(arguments[0]))a=arguments[0];else{a=new Array(b);for(var c=0;b>c;c++)a[c]=arguments[c]}return new vg(this,a)},c.prototype.createColdObservable=function(){var a,b=arguments.length;if(Array.isArray(arguments[0]))a=arguments[0];else{a=new Array(b);for(var c=0;b>c;c++)a[c]=arguments[c]}return new wg(this,a)},c.prototype.createResolvedPromise=function(a,b){return new Ga(this,[Qa.ReactiveTest.onNext(a,b),Qa.ReactiveTest.onCompleted(a)])},c.prototype.createRejectedPromise=function(a,b){return new Ga(this,[Qa.ReactiveTest.onError(a,b)])},c.prototype.createObserver=function(){return new ug(this)},c}(pg);var xg=Qa.AnonymousObservable=function(a){function b(a){return a&&Ya(a.dispose)?a:Ya(a)?cc(a):dc}function c(a,c){var d=c[0],f=c[1],g=$a(f.__subscribe).call(f,d);g!==Za||d.fail(Za.e)||e(Za.e),d.setDisposable(b(g))}function d(b,c){this.source=c,this.__subscribe=b,a.call(this)}return Wb(d,a),d.prototype._subscribe=function(a){var b=new yg(a),d=[b,this];return wc.scheduleRequired()?wc.schedule(d,c):c(null,d),b},d}(Sc),yg=function(a){function b(b){a.call(this),this.observer=b,this.m=new hc}Wb(b,a);var c=b.prototype;return c.next=function(a){var b=$a(this.observer.onNext).call(this.observer,a);b===Za&&(this.dispose(),e(b.e))},c.error=function(a){var b=$a(this.observer.onError).call(this.observer,a);this.dispose(),b===Za&&e(b.e)},c.completed=function(){var a=$a(this.observer.onCompleted).call(this.observer);this.dispose(),a===Za&&e(a.e)},c.setDisposable=function(a){this.m.setDisposable(a)},c.getDisposable=function(){return this.m.getDisposable()},c.dispose=function(){a.prototype.dispose.call(this),this.m.dispose()},b}(Nc),zg=function(a){function b(b,c){this._m=b,this._u=c,a.call(this)}return Wb(b,a),b.prototype.subscribeCore=function(a){return new jc(this._m.getDisposable(),this._u.subscribe(a))},b}(Tc),Ag=function(a){function b(b,c,d){a.call(this),this.key=b,this.underlyingObservable=d?new zg(d,c):c}return Wb(b,a),b.prototype._subscribe=function(a){return this.underlyingObservable.subscribe(a)},b}(Sc),Bg=Qa.Subject=function(a){function b(){a.call(this),this.isDisposed=!1,this.isStopped=!1,this.observers=[],this.hasError=!1}return Wb(b,a),Xb(b.prototype,Kc.prototype,{_subscribe:function(a){return fc(this),this.isStopped?this.hasError?(a.onError(this.error),dc):(a.onCompleted(),dc):(this.observers.push(a),new Cf(this,a))},hasObservers:function(){return fc(this),this.observers.length>0},onCompleted:function(){if(fc(this),!this.isStopped){this.isStopped=!0;for(var a=0,b=c(this.observers),d=b.length;d>a;a++)b[a].onCompleted();this.observers.length=0}},onError:function(a){if(fc(this),!this.isStopped){this.isStopped=!0,this.error=a,this.hasError=!0;for(var b=0,d=c(this.observers),e=d.length;e>b;b++)d[b].onError(a);this.observers.length=0}},onNext:function(a){if(fc(this),!this.isStopped)for(var b=0,d=c(this.observers),e=d.length;e>b;b++)d[b].onNext(a)},dispose:function(){this.isDisposed=!0,this.observers=null}}),b.create=function(a,b){return new Fg(a,b)},b}(Sc),Cg=Qa.AsyncSubject=function(a){function b(){a.call(this),this.isDisposed=!1,this.isStopped=!1,this.hasValue=!1,this.observers=[],this.hasError=!1}return Wb(b,a),Xb(b.prototype,Kc.prototype,{_subscribe:function(a){return fc(this),this.isStopped?(this.hasError?a.onError(this.error):this.hasValue?(a.onNext(this.value),a.onCompleted()):a.onCompleted(),dc):(this.observers.push(a),new Cf(this,a))},hasObservers:function(){return fc(this),this.observers.length>0},onCompleted:function(){var a,b;if(fc(this),!this.isStopped){this.isStopped=!0;var d=c(this.observers),b=d.length;if(this.hasValue)for(a=0;b>a;a++){var e=d[a];e.onNext(this.value),e.onCompleted()}else for(a=0;b>a;a++)d[a].onCompleted();this.observers.length=0}},onError:function(a){if(fc(this),!this.isStopped){this.isStopped=!0,this.hasError=!0,this.error=a;for(var b=0,d=c(this.observers),e=d.length;e>b;b++)d[b].onError(a);this.observers.length=0}},onNext:function(a){fc(this),this.isStopped||(this.value=a,this.hasValue=!0)},dispose:function(){this.isDisposed=!0,this.observers=null,this.error=null,this.value=null}}),b}(Sc),Dg=Qa.BehaviorSubject=function(a){function b(b){a.call(this),this.value=b,this.observers=[],this.isDisposed=!1,this.isStopped=!1,this.hasError=!1}return Wb(b,a),Xb(b.prototype,Kc.prototype,{_subscribe:function(a){return fc(this),this.isStopped?(this.hasError?a.onError(this.error):a.onCompleted(),dc):(this.observers.push(a),a.onNext(this.value),new Cf(this,a))},getValue:function(){return fc(this),this.hasError&&e(this.error),this.value},hasObservers:function(){return fc(this),this.observers.length>0},onCompleted:function(){if(fc(this),!this.isStopped){this.isStopped=!0;for(var a=0,b=c(this.observers),d=b.length;d>a;a++)b[a].onCompleted();this.observers.length=0}},onError:function(a){if(fc(this),!this.isStopped){this.isStopped=!0,this.hasError=!0,this.error=a;for(var b=0,d=c(this.observers),e=d.length;e>b;b++)d[b].onError(a);this.observers.length=0}},onNext:function(a){if(fc(this),!this.isStopped){this.value=a;for(var b=0,d=c(this.observers),e=d.length;e>b;b++)d[b].onNext(a)}},dispose:function(){this.isDisposed=!0,this.observers=null,this.value=null,this.error=null}}),b}(Sc),Eg=Qa.ReplaySubject=function(a){function b(a,b){return cc(function(){b.dispose(),!a.isDisposed&&a.observers.splice(a.observers.indexOf(b),1)})}function d(b,c,d){this.bufferSize=null==b?e:b,this.windowSize=null==c?e:c,this.scheduler=d||wc,this.q=[],this.observers=[],this.isStopped=!1,this.isDisposed=!1,this.hasError=!1,this.error=null,a.call(this)}var e=Math.pow(2,53)-1;return Wb(d,a),Xb(d.prototype,Kc.prototype,{_subscribe:function(a){fc(this);var c=new Qc(this.scheduler,a),d=b(this,c);this._trim(this.scheduler.now()),this.observers.push(c);for(var e=0,f=this.q.length;f>e;e++)c.onNext(this.q[e].value);return this.hasError?c.onError(this.error):this.isStopped&&c.onCompleted(),c.ensureActive(),d},hasObservers:function(){return fc(this),this.observers.length>0},_trim:function(a){for(;this.q.length>this.bufferSize;)this.q.shift();for(;this.q.length>0&&a-this.q[0].interval>this.windowSize;)this.q.shift()},onNext:function(a){if(fc(this),!this.isStopped){var b=this.scheduler.now();this.q.push({interval:b,value:a}),this._trim(b);for(var d=0,e=c(this.observers),f=e.length;f>d;d++){var g=e[d];g.onNext(a),g.ensureActive()}}},onError:function(a){if(fc(this),!this.isStopped){this.isStopped=!0,this.error=a,this.hasError=!0;var b=this.scheduler.now();this._trim(b);for(var d=0,e=c(this.observers),f=e.length;f>d;d++){var g=e[d];g.onError(a),g.ensureActive()}this.observers.length=0}},onCompleted:function(){if(fc(this),!this.isStopped){this.isStopped=!0;var a=this.scheduler.now();this._trim(a);for(var b=0,d=c(this.observers),e=d.length;e>b;b++){var f=d[b];f.onCompleted(),f.ensureActive()}this.observers.length=0}},dispose:function(){this.isDisposed=!0,this.observers=null}}),d}(Sc),Fg=Qa.AnonymousSubject=function(a){function b(b,c){this.observer=b,this.observable=c,a.call(this)}return Wb(b,a),Xb(b.prototype,Kc.prototype,{_subscribe:function(a){return this.observable.subscribe(a)},onCompleted:function(){this.observer.onCompleted()},onError:function(a){this.observer.onError(a)},onNext:function(a){this.observer.onNext(a)}}),b}(Sc);Qa.Pauser=function(a){function b(){a.call(this)}return Wb(b,a),b.prototype.pause=function(){this.onNext(!1)},b.prototype.resume=function(){this.onNext(!0)},b}(Bg),"function"==typeof define&&"object"==typeof define.amd&&define.amd?(Pa.Rx=Qa,define(function(){return Qa})):Ia&&Ja?Na?(Ja.exports=Qa).Rx=Qa:Ia.Rx=Qa:Pa.Rx=Qa;var Gg=j()}).call(this); //# sourceMappingURL=rx.all.map'use strict'; var has = Object.prototype.hasOwnProperty; /** * An auto incrementing id which we can use to create "unique" Ultron instances * so we can track the event emitters that are added through the Ultron * interface. * * @type {Number} * @private */ var id = 0; /** * Ultron is high-intelligence robot. It gathers intelligence so it can start improving * upon his rudimentary design. It will learn from your EventEmitting patterns * and exterminate them. * * @constructor * @param {EventEmitter} ee EventEmitter instance we need to wrap. * @api public */ function Ultron(ee) { if (!(this instanceof Ultron)) return new Ultron(ee); this.id = id++; this.ee = ee; } /** * Register a new EventListener for the given event. * * @param {String} event Name of the event. * @param {Functon} fn Callback function. * @param {Mixed} context The context of the function. * @returns {Ultron} * @api public */ Ultron.prototype.on = function on(event, fn, context) { fn.__ultron = this.id; this.ee.on(event, fn, context); return this; }; /** * Add an EventListener that's only called once. * * @param {String} event Name of the event. * @param {Function} fn Callback function. * @param {Mixed} context The context of the function. * @returns {Ultron} * @api public */ Ultron.prototype.once = function once(event, fn, context) { fn.__ultron = this.id; this.ee.once(event, fn, context); return this; }; /** * Remove the listeners we assigned for the given event. * * @returns {Ultron} * @api public */ Ultron.prototype.remove = function remove() { var args = arguments , ee = this.ee , event; // // When no event names are provided we assume that we need to clear all the // events that were assigned through us. // if (args.length === 1 && 'string' === typeof args[0]) { args = args[0].split(/[, ]+/); } else if (!args.length) { if (ee.eventNames) { args = ee.eventNames(); } else if (ee._events) { args = []; for (event in ee._events) { if (has.call(ee._events, event)) args.push(event); } if (Object.getOwnPropertySymbols) { args = args.concat(Object.getOwnPropertySymbols(ee._events)); } } } for (var i = 0; i < args.length; i++) { var listeners = ee.listeners(args[i]); for (var j = 0; j < listeners.length; j++) { event = listeners[j]; // // Once listeners have a `listener` property that stores the real listener // in the EventEmitter that ships with Node.js. // if (event.listener) { if (event.listener.__ultron !== this.id) continue; } else if (event.__ultron !== this.id) { continue; } ee.removeListener(args[i], event); } } return this; }; /** * Destroy the Ultron instance, remove all listeners and release all references. * * @returns {Boolean} * @api public */ Ultron.prototype.destroy = function destroy() { if (!this.ee) return false; this.remove(); this.ee = null; return true; }; // // Expose the module. // module.exports = Ultron; The MIT License (MIT) Copyright (c) 2015 Unshift.io, Arnout Kazemier, the 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. { "_args": [ [ "ultron@1.1.1", "C:\\Users\\wenju\\Documents\\projects\\core" ] ], "_from": "ultron@1.1.1", "_id": "ultron@1.1.1", "_inBundle": false, "_integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", "_location": "/ultron", "_phantomChildren": {}, "_requested": { "type": "version", "registry": true, "raw": "ultron@1.1.1", "name": "ultron", "escapedName": "ultron", "rawSpec": "1.1.1", "saveSpec": null, "fetchSpec": "1.1.1" }, "_requiredBy": [ "/" ], "_resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", "_spec": "1.1.1", "_where": "C:\\Users\\wenju\\Documents\\projects\\core", "author": { "name": "Arnout Kazemier" }, "bugs": { "url": "https://github.com/unshiftio/ultron/issues" }, "description": "Ultron is high-intelligence robot. It gathers intel so it can start improving upon his rudimentary design", "devDependencies": { "assume": "~1.5.0", "eventemitter3": "2.0.x", "istanbul": "0.4.x", "mocha": "~4.0.0", "pre-commit": "~1.2.0" }, "homepage": "https://github.com/unshiftio/ultron", "keywords": [ "Ultron", "robot", "gather", "intelligence", "event", "events", "eventemitter", "emitter", "cleanup" ], "license": "MIT", "main": "index.js", "name": "ultron", "repository": { "type": "git", "url": "git+https://github.com/unshiftio/ultron.git" }, "scripts": { "100%": "istanbul check-coverage --statements 100 --functions 100 --lines 100 --branches 100", "coverage": "istanbul cover _mocha -- test.js", "test": "mocha test.js", "test-travis": "istanbul cover _mocha --report lcovonly -- test.js", "watch": "mocha --watch test.js" }, "version": "1.1.1" } # Ultron [![Made by unshift](https://img.shields.io/badge/made%20by-unshift-00ffcc.svg?style=flat-square)](http://unshift.io)[![Version npm](http://img.shields.io/npm/v/ultron.svg?style=flat-square)](http://browsenpm.org/package/ultron)[![Build Status](http://img.shields.io/travis/unshiftio/ultron/master.svg?style=flat-square)](https://travis-ci.org/unshiftio/ultron)[![Dependencies](https://img.shields.io/david/unshiftio/ultron.svg?style=flat-square)](https://david-dm.org/unshiftio/ultron)[![Coverage Status](http://img.shields.io/coveralls/unshiftio/ultron/master.svg?style=flat-square)](https://coveralls.io/r/unshiftio/ultron?branch=master)[![IRC channel](http://img.shields.io/badge/IRC-irc.freenode.net%23unshift-00a8ff.svg?style=flat-square)](http://webchat.freenode.net/?channels=unshift) Ultron is a high-intelligence robot. It gathers intelligence so it can start improving upon his rudimentary design. It will learn your event emitting patterns and find ways to exterminate them. Allowing you to remove only the event emitters that **you** assigned and not the ones that your users or developers assigned. This can prevent race conditions, memory leaks and even file descriptor leaks from ever happening as you won't remove clean up processes. ## Installation The module is designed to be used in browsers using browserify and in Node.js. You can install the module through the public npm registry by running the following command in CLI: ``` npm install --save ultron ``` ## Usage In all examples we assume that you've required the library as following: ```js 'use strict'; var Ultron = require('ultron'); ``` Now that we've required the library we can construct our first `Ultron` instance. The constructor requires one argument which should be the `EventEmitter` instance that we need to operate upon. This can be the `EventEmitter` module that ships with Node.js or `EventEmitter3` or anything else as long as it follow the same API and internal structure as these 2. So with that in mind we can create the instance: ```js // // For the sake of this example we're going to construct an empty EventEmitter // var EventEmitter = require('events').EventEmitter; // or require('eventmitter3'); var events = new EventEmitter(); var ultron = new Ultron(events); ``` You can now use the following API's from the Ultron instance: ### Ultron.on Register a new event listener for the given event. It follows the exact same API as `EventEmitter.on` but it will return itself instead of returning the EventEmitter instance. If you are using EventEmitter3 it also supports the context param: ```js ultron.on('event-name', handler, { custom: 'function context' }); ``` Just like you would expect, it can also be chained together. ```js ultron .on('event-name', handler) .on('another event', handler); ``` ### Ultron.once Exactly the same as the [Ultron.on](#ultronon) but it only allows the execution once. Just like you would expect, it can also be chained together. ```js ultron .once('event-name', handler, { custom: 'this value' }) .once('another event', handler); ``` ### Ultron.remove This is where all the magic happens and the safe removal starts. This function accepts different argument styles: - No arguments, assume that all events need to be removed so it will work as `removeAllListeners()` API. - 1 argument, when it's a string it will be split on ` ` and `,` to create a list of events that need to be cleared. - Multiple arguments, we assume that they are all names of events that need to be cleared. ```js ultron.remove('foo, bar baz'); // Removes foo, bar and baz. ultron.remove('foo', 'bar', 'baz'); // Removes foo, bar and baz. ultron.remove(); // Removes everything. ``` If you just want to remove a single event listener using a function reference you can still use the EventEmitter's `removeListener(event, fn)` API: ```js function foo() {} ultron.on('foo', foo); events.removeListener('foo', foo); ``` ## License MIT // Underscore.js 1.9.1 // http://underscorejs.org // (c) 2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. !function(){var n="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global||this||{},r=n._,e=Array.prototype,o=Object.prototype,s="undefined"!=typeof Symbol?Symbol.prototype:null,u=e.push,c=e.slice,p=o.toString,i=o.hasOwnProperty,t=Array.isArray,a=Object.keys,l=Object.create,f=function(){},h=function(n){return n instanceof h?n:this instanceof h?void(this._wrapped=n):new h(n)};"undefined"==typeof exports||exports.nodeType?n._=h:("undefined"!=typeof module&&!module.nodeType&&module.exports&&(exports=module.exports=h),exports._=h),h.VERSION="1.9.1";var v,y=function(u,i,n){if(void 0===i)return u;switch(null==n?3:n){case 1:return function(n){return u.call(i,n)};case 3:return function(n,r,t){return u.call(i,n,r,t)};case 4:return function(n,r,t,e){return u.call(i,n,r,t,e)}}return function(){return u.apply(i,arguments)}},d=function(n,r,t){return h.iteratee!==v?h.iteratee(n,r):null==n?h.identity:h.isFunction(n)?y(n,r,t):h.isObject(n)&&!h.isArray(n)?h.matcher(n):h.property(n)};h.iteratee=v=function(n,r){return d(n,r,1/0)};var g=function(u,i){return i=null==i?u.length-1:+i,function(){for(var n=Math.max(arguments.length-i,0),r=Array(n),t=0;t":">",'"':""","'":"'","`":"`"},P=h.invert(L),W=function(r){var t=function(n){return r[n]},n="(?:"+h.keys(r).join("|")+")",e=RegExp(n),u=RegExp(n,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};h.escape=W(L),h.unescape=W(P),h.result=function(n,r,t){h.isArray(r)||(r=[r]);var e=r.length;if(!e)return h.isFunction(t)?t.call(n):t;for(var u=0;u/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var J=/(.)^/,U={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},V=/\\|'|\r|\n|\u2028|\u2029/g,$=function(n){return"\\"+U[n]};h.template=function(i,n,r){!n&&r&&(n=r),n=h.defaults({},n,h.templateSettings);var t,e=RegExp([(n.escape||J).source,(n.interpolate||J).source,(n.evaluate||J).source].join("|")+"|$","g"),o=0,a="__p+='";i.replace(e,function(n,r,t,e,u){return a+=i.slice(o,u).replace(V,$),o=u+n.length,r?a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":t?a+="'+\n((__t=("+t+"))==null?'':__t)+\n'":e&&(a+="';\n"+e+"\n__p+='"),n}),a+="';\n",n.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{t=new Function(n.variable||"obj","_",a)}catch(n){throw n.source=a,n}var u=function(n){return t.call(this,n,h)},c=n.variable||"obj";return u.source="function("+c+"){\n"+a+"}",u},h.chain=function(n){var r=h(n);return r._chain=!0,r};var G=function(n,r){return n._chain?h(r).chain():r};h.mixin=function(t){return h.each(h.functions(t),function(n){var r=h[n]=t[n];h.prototype[n]=function(){var n=[this._wrapped];return u.apply(n,arguments),G(this,r.apply(h,n))}}),h},h.mixin(h),h.each(["pop","push","reverse","shift","sort","splice","unshift"],function(r){var t=e[r];h.prototype[r]=function(){var n=this._wrapped;return t.apply(n,arguments),"shift"!==r&&"splice"!==r||0!==n.length||delete n[0],G(this,n)}}),h.each(["concat","join","slice"],function(n){var r=e[n];h.prototype[n]=function(){return G(this,r.apply(this._wrapped,arguments))}}),h.prototype.value=function(){return this._wrapped},h.prototype.valueOf=h.prototype.toJSON=h.prototype.value,h.prototype.toString=function(){return String(this._wrapped)},"function"==typeof define&&define.amd&&define("underscore",[],function(){return h})}();'use strict'; /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ var WS = module.exports = require('./lib/WebSocket'); WS.Server = require('./lib/WebSocketServer'); WS.Sender = require('./lib/Sender'); WS.Receiver = require('./lib/Receiver'); /** * Create a new WebSocket server. * * @param {Object} options Server options * @param {Function} fn Optional connection listener. * @returns {WS.Server} * @api public */ WS.createServer = function createServer(options, fn) { var server = new WS.Server(options); if (typeof fn === 'function') { server.on('connection', fn); } return server; }; /** * Create a new WebSocket connection. * * @param {String} address The URL/address we need to connect to. * @param {Function} fn Open listener. * @returns {WS} * @api public */ WS.connect = WS.createConnection = function connect(address, fn) { var client = new WS(address); if (typeof fn === 'function') { client.on('open', fn); } return client; }; /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ var util = require('util'); function BufferPool(initialSize, growStrategy, shrinkStrategy) { if (this instanceof BufferPool === false) { throw new TypeError("Classes can't be function-called"); } if (typeof initialSize === 'function') { shrinkStrategy = growStrategy; growStrategy = initialSize; initialSize = 0; } else if (typeof initialSize === 'undefined') { initialSize = 0; } this._growStrategy = (growStrategy || function(db, size) { return db.used + size; }).bind(null, this); this._shrinkStrategy = (shrinkStrategy || function(db) { return initialSize; }).bind(null, this); this._buffer = initialSize ? new Buffer(initialSize) : null; this._offset = 0; this._used = 0; this._changeFactor = 0; this.__defineGetter__('size', function(){ return this._buffer == null ? 0 : this._buffer.length; }); this.__defineGetter__('used', function(){ return this._used; }); } BufferPool.prototype.get = function(length) { if (this._buffer == null || this._offset + length > this._buffer.length) { var newBuf = new Buffer(this._growStrategy(length)); this._buffer = newBuf; this._offset = 0; } this._used += length; var buf = this._buffer.slice(this._offset, this._offset + length); this._offset += length; return buf; } BufferPool.prototype.reset = function(forceNewBuffer) { var len = this._shrinkStrategy(); if (len < this.size) this._changeFactor -= 1; if (forceNewBuffer || this._changeFactor < -2) { this._changeFactor = 0; this._buffer = len ? new Buffer(len) : null; } this._offset = 0; this._used = 0; } module.exports = BufferPool; /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ exports.BufferUtil = { merge: function(mergedBuffer, buffers) { var offset = 0; for (var i = 0, l = buffers.length; i < l; ++i) { var buf = buffers[i]; buf.copy(mergedBuffer, offset); offset += buf.length; } }, mask: function(source, mask, output, offset, length) { var maskNum = mask.readUInt32LE(0, true); var i = 0; for (; i < length - 3; i += 4) { var num = maskNum ^ source.readUInt32LE(i, true); if (num < 0) num = 4294967296 + num; output.writeUInt32LE(num, offset + i, true); } switch (length % 4) { case 3: output[offset + i + 2] = source[i + 2] ^ mask[2]; case 2: output[offset + i + 1] = source[i + 1] ^ mask[1]; case 1: output[offset + i] = source[i] ^ mask[0]; case 0:; } }, unmask: function(data, mask) { var maskNum = mask.readUInt32LE(0, true); var length = data.length; var i = 0; for (; i < length - 3; i += 4) { var num = maskNum ^ data.readUInt32LE(i, true); if (num < 0) num = 4294967296 + num; data.writeUInt32LE(num, i, true); } switch (length % 4) { case 3: data[i + 2] = data[i + 2] ^ mask[2]; case 2: data[i + 1] = data[i + 1] ^ mask[1]; case 1: data[i] = data[i] ^ mask[0]; case 0:; } } } 'use strict'; /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ try { module.exports = require('bufferutil'); } catch (e) { module.exports = require('./BufferUtil.fallback'); } /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ module.exports = { isValidErrorCode: function(code) { return (code >= 1000 && code <= 1011 && code != 1004 && code != 1005 && code != 1006) || (code >= 3000 && code <= 4999); }, 1000: 'normal', 1001: 'going away', 1002: 'protocol error', 1003: 'unsupported data', 1004: 'reserved', 1005: 'reserved for extensions', 1006: 'reserved for extensions', 1007: 'inconsistent or invalid data', 1008: 'policy violation', 1009: 'message too big', 1010: 'extension handshake missing', 1011: 'an unexpected condition prevented the request from being fulfilled', }; var util = require('util'); /** * Module exports. */ exports.parse = parse; exports.format = format; /** * Parse extensions header value */ function parse(value) { value = value || ''; var extensions = {}; value.split(',').forEach(function(v) { var params = v.split(';'); var token = params.shift().trim(); var paramsList = extensions[token] = extensions[token] || []; var parsedParams = {}; params.forEach(function(param) { var parts = param.trim().split('='); var key = parts[0]; var value = parts[1]; if (typeof value === 'undefined') { value = true; } else { // unquote value if (value[0] === '"') { value = value.slice(1); } if (value[value.length - 1] === '"') { value = value.slice(0, value.length - 1); } } (parsedParams[key] = parsedParams[key] || []).push(value); }); paramsList.push(parsedParams); }); return extensions; } /** * Format extensions header value */ function format(value) { return Object.keys(value).map(function(token) { var paramsList = value[token]; if (!util.isArray(paramsList)) { paramsList = [paramsList]; } return paramsList.map(function(params) { return [token].concat(Object.keys(params).map(function(k) { var p = params[k]; if (!util.isArray(p)) p = [p]; return p.map(function(v) { return v === true ? k : k + '=' + v; }).join('; '); })).join('; '); }).join(', '); }).join(', '); } var zlib = require('zlib'); var AVAILABLE_WINDOW_BITS = [8, 9, 10, 11, 12, 13, 14, 15]; var DEFAULT_WINDOW_BITS = 15; var DEFAULT_MEM_LEVEL = 8; PerMessageDeflate.extensionName = 'permessage-deflate'; /** * Per-message Compression Extensions implementation */ function PerMessageDeflate(options, isServer,maxPayload) { if (this instanceof PerMessageDeflate === false) { throw new TypeError("Classes can't be function-called"); } this._options = options || {}; this._isServer = !!isServer; this._inflate = null; this._deflate = null; this.params = null; this._maxPayload = maxPayload || 0; } /** * Create extension parameters offer * * @api public */ PerMessageDeflate.prototype.offer = function() { var params = {}; if (this._options.serverNoContextTakeover) { params.server_no_context_takeover = true; } if (this._options.clientNoContextTakeover) { params.client_no_context_takeover = true; } if (this._options.serverMaxWindowBits) { params.server_max_window_bits = this._options.serverMaxWindowBits; } if (this._options.clientMaxWindowBits) { params.client_max_window_bits = this._options.clientMaxWindowBits; } else if (this._options.clientMaxWindowBits == null) { params.client_max_window_bits = true; } return params; }; /** * Accept extension offer * * @api public */ PerMessageDeflate.prototype.accept = function(paramsList) { paramsList = this.normalizeParams(paramsList); var params; if (this._isServer) { params = this.acceptAsServer(paramsList); } else { params = this.acceptAsClient(paramsList); } this.params = params; return params; }; /** * Releases all resources used by the extension * * @api public */ PerMessageDeflate.prototype.cleanup = function() { if (this._inflate) { if (this._inflate.writeInProgress) { this._inflate.pendingClose = true; } else { if (this._inflate.close) this._inflate.close(); this._inflate = null; } } if (this._deflate) { if (this._deflate.writeInProgress) { this._deflate.pendingClose = true; } else { if (this._deflate.close) this._deflate.close(); this._deflate = null; } } }; /** * Accept extension offer from client * * @api private */ PerMessageDeflate.prototype.acceptAsServer = function(paramsList) { var accepted = {}; var result = paramsList.some(function(params) { accepted = {}; if (this._options.serverNoContextTakeover === false && params.server_no_context_takeover) { return; } if (this._options.serverMaxWindowBits === false && params.server_max_window_bits) { return; } if (typeof this._options.serverMaxWindowBits === 'number' && typeof params.server_max_window_bits === 'number' && this._options.serverMaxWindowBits > params.server_max_window_bits) { return; } if (typeof this._options.clientMaxWindowBits === 'number' && !params.client_max_window_bits) { return; } if (this._options.serverNoContextTakeover || params.server_no_context_takeover) { accepted.server_no_context_takeover = true; } if (this._options.clientNoContextTakeover) { accepted.client_no_context_takeover = true; } if (this._options.clientNoContextTakeover !== false && params.client_no_context_takeover) { accepted.client_no_context_takeover = true; } if (typeof this._options.serverMaxWindowBits === 'number') { accepted.server_max_window_bits = this._options.serverMaxWindowBits; } else if (typeof params.server_max_window_bits === 'number') { accepted.server_max_window_bits = params.server_max_window_bits; } if (typeof this._options.clientMaxWindowBits === 'number') { accepted.client_max_window_bits = this._options.clientMaxWindowBits; } else if (this._options.clientMaxWindowBits !== false && typeof params.client_max_window_bits === 'number') { accepted.client_max_window_bits = params.client_max_window_bits; } return true; }, this); if (!result) { throw new Error('Doesn\'t support the offered configuration'); } return accepted; }; /** * Accept extension response from server * * @api privaye */ PerMessageDeflate.prototype.acceptAsClient = function(paramsList) { var params = paramsList[0]; if (this._options.clientNoContextTakeover != null) { if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) { throw new Error('Invalid value for "client_no_context_takeover"'); } } if (this._options.clientMaxWindowBits != null) { if (this._options.clientMaxWindowBits === false && params.client_max_window_bits) { throw new Error('Invalid value for "client_max_window_bits"'); } if (typeof this._options.clientMaxWindowBits === 'number' && (!params.client_max_window_bits || params.client_max_window_bits > this._options.clientMaxWindowBits)) { throw new Error('Invalid value for "client_max_window_bits"'); } } return params; }; /** * Normalize extensions parameters * * @api private */ PerMessageDeflate.prototype.normalizeParams = function(paramsList) { return paramsList.map(function(params) { Object.keys(params).forEach(function(key) { var value = params[key]; if (value.length > 1) { throw new Error('Multiple extension parameters for ' + key); } value = value[0]; switch (key) { case 'server_no_context_takeover': case 'client_no_context_takeover': if (value !== true) { throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')'); } params[key] = true; break; case 'server_max_window_bits': case 'client_max_window_bits': if (typeof value === 'string') { value = parseInt(value, 10); if (!~AVAILABLE_WINDOW_BITS.indexOf(value)) { throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')'); } } if (!this._isServer && value === true) { throw new Error('Missing extension parameter value for ' + key); } params[key] = value; break; default: throw new Error('Not defined extension parameter (' + key + ')'); } }, this); return params; }, this); }; /** * Decompress message * * @api public */ PerMessageDeflate.prototype.decompress = function (data, fin, callback) { var endpoint = this._isServer ? 'client' : 'server'; if (!this._inflate) { var maxWindowBits = this.params[endpoint + '_max_window_bits']; this._inflate = zlib.createInflateRaw({ windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS }); } this._inflate.writeInProgress = true; var self = this; var buffers = []; var cumulativeBufferLength=0; this._inflate.on('error', onError).on('data', onData); this._inflate.write(data); if (fin) { this._inflate.write(new Buffer([0x00, 0x00, 0xff, 0xff])); } this._inflate.flush(function() { cleanup(); callback(null, Buffer.concat(buffers)); }); function onError(err) { cleanup(); callback(err); } function onData(data) { if(self._maxPayload!==undefined && self._maxPayload!==null && self._maxPayload>0){ cumulativeBufferLength+=data.length; if(cumulativeBufferLength>self._maxPayload){ buffers=[]; cleanup(); var err={type:1009}; callback(err); return; } } buffers.push(data); } function cleanup() { if (!self._inflate) return; self._inflate.removeListener('error', onError); self._inflate.removeListener('data', onData); self._inflate.writeInProgress = false; if ((fin && self.params[endpoint + '_no_context_takeover']) || self._inflate.pendingClose) { if (self._inflate.close) self._inflate.close(); self._inflate = null; } } }; /** * Compress message * * @api public */ PerMessageDeflate.prototype.compress = function (data, fin, callback) { var endpoint = this._isServer ? 'server' : 'client'; if (!this._deflate) { var maxWindowBits = this.params[endpoint + '_max_window_bits']; this._deflate = zlib.createDeflateRaw({ flush: zlib.Z_SYNC_FLUSH, windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS, memLevel: this._options.memLevel || DEFAULT_MEM_LEVEL }); } this._deflate.writeInProgress = true; var self = this; var buffers = []; this._deflate.on('error', onError).on('data', onData); this._deflate.write(data); this._deflate.flush(function() { cleanup(); var data = Buffer.concat(buffers); if (fin) { data = data.slice(0, data.length - 4); } callback(null, data); }); function onError(err) { cleanup(); callback(err); } function onData(data) { buffers.push(data); } function cleanup() { if (!self._deflate) return; self._deflate.removeListener('error', onError); self._deflate.removeListener('data', onData); self._deflate.writeInProgress = false; if ((fin && self.params[endpoint + '_no_context_takeover']) || self._deflate.pendingClose) { if (self._deflate.close) self._deflate.close(); self._deflate = null; } } }; module.exports = PerMessageDeflate; /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ var util = require('util'); /** * State constants */ var EMPTY = 0 , BODY = 1; var BINARYLENGTH = 2 , BINARYBODY = 3; /** * Hixie Receiver implementation */ function Receiver () { if (this instanceof Receiver === false) { throw new TypeError("Classes can't be function-called"); } this.state = EMPTY; this.buffers = []; this.messageEnd = -1; this.spanLength = 0; this.dead = false; this.onerror = function() {}; this.ontext = function() {}; this.onbinary = function() {}; this.onclose = function() {}; this.onping = function() {}; this.onpong = function() {}; } module.exports = Receiver; /** * Add new data to the parser. * * @api public */ Receiver.prototype.add = function(data) { if (this.dead) return; var self = this; function doAdd() { if (self.state === EMPTY) { if (data.length == 2 && data[0] == 0xFF && data[1] == 0x00) { self.reset(); self.onclose(); return; } if (data[0] === 0x80) { self.messageEnd = 0; self.state = BINARYLENGTH; data = data.slice(1); } else { if (data[0] !== 0x00) { self.error('payload must start with 0x00 byte', true); return; } data = data.slice(1); self.state = BODY; } } if (self.state === BINARYLENGTH) { var i = 0; while ((i < data.length) && (data[i] & 0x80)) { self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f); ++i; } if (i < data.length) { self.messageEnd = 128 * self.messageEnd + (data[i] & 0x7f); self.state = BINARYBODY; ++i; } if (i > 0) data = data.slice(i); } if (self.state === BINARYBODY) { var dataleft = self.messageEnd - self.spanLength; if (data.length >= dataleft) { // consume the whole buffer to finish the frame self.buffers.push(data); self.spanLength += dataleft; self.messageEnd = dataleft; return self.parse(); } // frame's not done even if we consume it all self.buffers.push(data); self.spanLength += data.length; return; } self.buffers.push(data); if ((self.messageEnd = bufferIndex(data, 0xFF)) != -1) { self.spanLength += self.messageEnd; return self.parse(); } else self.spanLength += data.length; } while(data) data = doAdd(); }; /** * Releases all resources used by the receiver. * * @api public */ Receiver.prototype.cleanup = function() { this.dead = true; this.state = EMPTY; this.buffers = []; }; /** * Process buffered data. * * @api public */ Receiver.prototype.parse = function() { var output = new Buffer(this.spanLength); var outputIndex = 0; for (var bi = 0, bl = this.buffers.length; bi < bl - 1; ++bi) { var buffer = this.buffers[bi]; buffer.copy(output, outputIndex); outputIndex += buffer.length; } var lastBuffer = this.buffers[this.buffers.length - 1]; if (this.messageEnd > 0) lastBuffer.copy(output, outputIndex, 0, this.messageEnd); if (this.state !== BODY) --this.messageEnd; var tail = null; if (this.messageEnd < lastBuffer.length - 1) { tail = lastBuffer.slice(this.messageEnd + 1); } this.reset(); this.ontext(output.toString('utf8')); return tail; }; /** * Handles an error * * @api private */ Receiver.prototype.error = function (reason, terminate) { if (this.dead) return; this.reset(); if(typeof reason == 'string'){ this.onerror(new Error(reason), terminate); } else if(reason.constructor == Error){ this.onerror(reason, terminate); } else{ this.onerror(new Error("An error occured"),terminate); } return this; }; /** * Reset parser state * * @api private */ Receiver.prototype.reset = function (reason) { if (this.dead) return; this.state = EMPTY; this.buffers = []; this.messageEnd = -1; this.spanLength = 0; }; /** * Internal api */ function bufferIndex(buffer, byte) { for (var i = 0, l = buffer.length; i < l; ++i) { if (buffer[i] === byte) return i; } return -1; } /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ var util = require('util') , Validation = require('./Validation').Validation , ErrorCodes = require('./ErrorCodes') , BufferPool = require('./BufferPool') , bufferUtil = require('./BufferUtil').BufferUtil , PerMessageDeflate = require('./PerMessageDeflate'); /** * HyBi Receiver implementation */ function Receiver (extensions,maxPayload) { if (this instanceof Receiver === false) { throw new TypeError("Classes can't be function-called"); } if(typeof extensions==='number'){ maxPayload=extensions; extensions={}; } // memory pool for fragmented messages var fragmentedPoolPrevUsed = -1; this.fragmentedBufferPool = new BufferPool(1024, function(db, length) { return db.used + length; }, function(db) { return fragmentedPoolPrevUsed = fragmentedPoolPrevUsed >= 0 ? Math.ceil((fragmentedPoolPrevUsed + db.used) / 2) : db.used; }); // memory pool for unfragmented messages var unfragmentedPoolPrevUsed = -1; this.unfragmentedBufferPool = new BufferPool(1024, function(db, length) { return db.used + length; }, function(db) { return unfragmentedPoolPrevUsed = unfragmentedPoolPrevUsed >= 0 ? Math.ceil((unfragmentedPoolPrevUsed + db.used) / 2) : db.used; }); this.extensions = extensions || {}; this.maxPayload = maxPayload || 0; this.currentPayloadLength = 0; this.state = { activeFragmentedOperation: null, lastFragment: false, masked: false, opcode: 0, fragmentedOperation: false }; this.overflow = []; this.headerBuffer = new Buffer(10); this.expectOffset = 0; this.expectBuffer = null; this.expectHandler = null; this.currentMessage = []; this.currentMessageLength = 0; this.messageHandlers = []; this.expectHeader(2, this.processPacket); this.dead = false; this.processing = false; this.onerror = function() {}; this.ontext = function() {}; this.onbinary = function() {}; this.onclose = function() {}; this.onping = function() {}; this.onpong = function() {}; } module.exports = Receiver; /** * Add new data to the parser. * * @api public */ Receiver.prototype.add = function(data) { if (this.dead) return; var dataLength = data.length; if (dataLength == 0) return; if (this.expectBuffer == null) { this.overflow.push(data); return; } var toRead = Math.min(dataLength, this.expectBuffer.length - this.expectOffset); fastCopy(toRead, data, this.expectBuffer, this.expectOffset); this.expectOffset += toRead; if (toRead < dataLength) { this.overflow.push(data.slice(toRead)); } while (this.expectBuffer && this.expectOffset == this.expectBuffer.length) { var bufferForHandler = this.expectBuffer; this.expectBuffer = null; this.expectOffset = 0; this.expectHandler.call(this, bufferForHandler); } }; /** * Releases all resources used by the receiver. * * @api public */ Receiver.prototype.cleanup = function() { this.dead = true; this.overflow = null; this.headerBuffer = null; this.expectBuffer = null; this.expectHandler = null; this.unfragmentedBufferPool = null; this.fragmentedBufferPool = null; this.state = null; this.currentMessage = null; this.onerror = null; this.ontext = null; this.onbinary = null; this.onclose = null; this.onping = null; this.onpong = null; }; /** * Waits for a certain amount of header bytes to be available, then fires a callback. * * @api private */ Receiver.prototype.expectHeader = function(length, handler) { if (length == 0) { handler(null); return; } this.expectBuffer = this.headerBuffer.slice(this.expectOffset, this.expectOffset + length); this.expectHandler = handler; var toRead = length; while (toRead > 0 && this.overflow.length > 0) { var fromOverflow = this.overflow.pop(); if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead)); var read = Math.min(fromOverflow.length, toRead); fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset); this.expectOffset += read; toRead -= read; } }; /** * Waits for a certain amount of data bytes to be available, then fires a callback. * * @api private */ Receiver.prototype.expectData = function(length, handler) { if (length == 0) { handler(null); return; } this.expectBuffer = this.allocateFromPool(length, this.state.fragmentedOperation); this.expectHandler = handler; var toRead = length; while (toRead > 0 && this.overflow.length > 0) { var fromOverflow = this.overflow.pop(); if (toRead < fromOverflow.length) this.overflow.push(fromOverflow.slice(toRead)); var read = Math.min(fromOverflow.length, toRead); fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset); this.expectOffset += read; toRead -= read; } }; /** * Allocates memory from the buffer pool. * * @api private */ Receiver.prototype.allocateFromPool = function(length, isFragmented) { return (isFragmented ? this.fragmentedBufferPool : this.unfragmentedBufferPool).get(length); }; /** * Start processing a new packet. * * @api private */ Receiver.prototype.processPacket = function (data) { if (this.extensions[PerMessageDeflate.extensionName]) { if ((data[0] & 0x30) != 0) { this.error('reserved fields (2, 3) must be empty', 1002); return; } } else { if ((data[0] & 0x70) != 0) { this.error('reserved fields must be empty', 1002); return; } } this.state.lastFragment = (data[0] & 0x80) == 0x80; this.state.masked = (data[1] & 0x80) == 0x80; var compressed = (data[0] & 0x40) == 0x40; var opcode = data[0] & 0xf; if (opcode === 0) { if (compressed) { this.error('continuation frame cannot have the Per-message Compressed bits', 1002); return; } // continuation frame this.state.fragmentedOperation = true; this.state.opcode = this.state.activeFragmentedOperation; if (!(this.state.opcode == 1 || this.state.opcode == 2)) { this.error('continuation frame cannot follow current opcode', 1002); return; } } else { if (opcode < 3 && this.state.activeFragmentedOperation != null) { this.error('data frames after the initial data frame must have opcode 0', 1002); return; } if (opcode >= 8 && compressed) { this.error('control frames cannot have the Per-message Compressed bits', 1002); return; } this.state.compressed = compressed; this.state.opcode = opcode; if (this.state.lastFragment === false) { this.state.fragmentedOperation = true; this.state.activeFragmentedOperation = opcode; } else this.state.fragmentedOperation = false; } var handler = opcodes[this.state.opcode]; if (typeof handler == 'undefined') this.error('no handler for opcode ' + this.state.opcode, 1002); else { handler.start.call(this, data); } }; /** * Endprocessing a packet. * * @api private */ Receiver.prototype.endPacket = function() { if (this.dead) return; if (!this.state.fragmentedOperation) this.unfragmentedBufferPool.reset(true); else if (this.state.lastFragment) this.fragmentedBufferPool.reset(true); this.expectOffset = 0; this.expectBuffer = null; this.expectHandler = null; if (this.state.lastFragment && this.state.opcode === this.state.activeFragmentedOperation) { // end current fragmented operation this.state.activeFragmentedOperation = null; } this.currentPayloadLength = 0; this.state.lastFragment = false; this.state.opcode = this.state.activeFragmentedOperation != null ? this.state.activeFragmentedOperation : 0; this.state.masked = false; this.expectHeader(2, this.processPacket); }; /** * Reset the parser state. * * @api private */ Receiver.prototype.reset = function() { if (this.dead) return; this.state = { activeFragmentedOperation: null, lastFragment: false, masked: false, opcode: 0, fragmentedOperation: false }; this.fragmentedBufferPool.reset(true); this.unfragmentedBufferPool.reset(true); this.expectOffset = 0; this.expectBuffer = null; this.expectHandler = null; this.overflow = []; this.currentMessage = []; this.currentMessageLength = 0; this.messageHandlers = []; this.currentPayloadLength = 0; }; /** * Unmask received data. * * @api private */ Receiver.prototype.unmask = function (mask, buf, binary) { if (mask != null && buf != null) bufferUtil.unmask(buf, mask); if (binary) return buf; return buf != null ? buf.toString('utf8') : ''; }; /** * Handles an error * * @api private */ Receiver.prototype.error = function (reason, protocolErrorCode) { if (this.dead) return; this.reset(); if(typeof reason == 'string'){ this.onerror(new Error(reason), protocolErrorCode); } else if(reason.constructor == Error){ this.onerror(reason, protocolErrorCode); } else{ this.onerror(new Error("An error occured"),protocolErrorCode); } return this; }; /** * Execute message handler buffers * * @api private */ Receiver.prototype.flush = function() { if (this.processing || this.dead) return; var handler = this.messageHandlers.shift(); if (!handler) return; this.processing = true; var self = this; handler(function() { self.processing = false; self.flush(); }); }; /** * Apply extensions to message * * @api private */ Receiver.prototype.applyExtensions = function(messageBuffer, fin, compressed, callback) { var self = this; if (compressed) { this.extensions[PerMessageDeflate.extensionName].decompress(messageBuffer, fin, function(err, buffer) { if (self.dead) return; if (err) { callback(new Error('invalid compressed data')); return; } callback(null, buffer); }); } else { callback(null, messageBuffer); } }; /** * Checks payload size, disconnects socket when it exceeds maxPayload * * @api private */ Receiver.prototype.maxPayloadExceeded = function(length) { if (this.maxPayload=== undefined || this.maxPayload === null || this.maxPayload < 1) { return false; } var fullLength = this.currentPayloadLength + length; if (fullLength < this.maxPayload) { this.currentPayloadLength = fullLength; return false; } this.error('payload cannot exceed ' + this.maxPayload + ' bytes', 1009); this.messageBuffer=[]; this.cleanup(); return true; }; /** * Buffer utilities */ function readUInt16BE(start) { return (this[start]<<8) + this[start+1]; } function readUInt32BE(start) { return (this[start]<<24) + (this[start+1]<<16) + (this[start+2]<<8) + this[start+3]; } function fastCopy(length, srcBuffer, dstBuffer, dstOffset) { switch (length) { default: srcBuffer.copy(dstBuffer, dstOffset, 0, length); break; case 16: dstBuffer[dstOffset+15] = srcBuffer[15]; case 15: dstBuffer[dstOffset+14] = srcBuffer[14]; case 14: dstBuffer[dstOffset+13] = srcBuffer[13]; case 13: dstBuffer[dstOffset+12] = srcBuffer[12]; case 12: dstBuffer[dstOffset+11] = srcBuffer[11]; case 11: dstBuffer[dstOffset+10] = srcBuffer[10]; case 10: dstBuffer[dstOffset+9] = srcBuffer[9]; case 9: dstBuffer[dstOffset+8] = srcBuffer[8]; case 8: dstBuffer[dstOffset+7] = srcBuffer[7]; case 7: dstBuffer[dstOffset+6] = srcBuffer[6]; case 6: dstBuffer[dstOffset+5] = srcBuffer[5]; case 5: dstBuffer[dstOffset+4] = srcBuffer[4]; case 4: dstBuffer[dstOffset+3] = srcBuffer[3]; case 3: dstBuffer[dstOffset+2] = srcBuffer[2]; case 2: dstBuffer[dstOffset+1] = srcBuffer[1]; case 1: dstBuffer[dstOffset] = srcBuffer[0]; } } function clone(obj) { var cloned = {}; for (var k in obj) { if (obj.hasOwnProperty(k)) { cloned[k] = obj[k]; } } return cloned; } /** * Opcode handlers */ var opcodes = { // text '1': { start: function(data) { var self = this; // decode length var firstLength = data[1] & 0x7f; if (firstLength < 126) { if (self.maxPayloadExceeded(firstLength)){ self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009); return; } opcodes['1'].getData.call(self, firstLength); } else if (firstLength == 126) { self.expectHeader(2, function(data) { var length = readUInt16BE.call(data, 0); if (self.maxPayloadExceeded(length)){ self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009); return; } opcodes['1'].getData.call(self, length); }); } else if (firstLength == 127) { self.expectHeader(8, function(data) { if (readUInt32BE.call(data, 0) != 0) { self.error('packets with length spanning more than 32 bit is currently not supported', 1008); return; } var length = readUInt32BE.call(data, 4); if (self.maxPayloadExceeded(length)){ self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009); return; } opcodes['1'].getData.call(self, readUInt32BE.call(data, 4)); }); } }, getData: function(length) { var self = this; if (self.state.masked) { self.expectHeader(4, function(data) { var mask = data; self.expectData(length, function(data) { opcodes['1'].finish.call(self, mask, data); }); }); } else { self.expectData(length, function(data) { opcodes['1'].finish.call(self, null, data); }); } }, finish: function(mask, data) { var self = this; var packet = this.unmask(mask, data, true) || new Buffer(0); var state = clone(this.state); this.messageHandlers.push(function(callback) { self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) { if (err) { if(err.type===1009){ return self.error('Maximumpayload exceeded in compressed text message. Aborting...', 1009); } return self.error(err.message, 1007); } if (buffer != null) { if( self.maxPayload==0 || (self.maxPayload > 0 && (self.currentMessageLength + buffer.length) < self.maxPayload) ){ self.currentMessage.push(buffer); } else{ self.currentMessage=null; self.currentMessage = []; self.currentMessageLength = 0; self.error(new Error('Maximum payload exceeded. maxPayload: '+self.maxPayload), 1009); return; } self.currentMessageLength += buffer.length; } if (state.lastFragment) { var messageBuffer = Buffer.concat(self.currentMessage); self.currentMessage = []; self.currentMessageLength = 0; if (!Validation.isValidUTF8(messageBuffer)) { self.error('invalid utf8 sequence', 1007); return; } self.ontext(messageBuffer.toString('utf8'), {masked: state.masked, buffer: messageBuffer}); } callback(); }); }); this.flush(); this.endPacket(); } }, // binary '2': { start: function(data) { var self = this; // decode length var firstLength = data[1] & 0x7f; if (firstLength < 126) { if (self.maxPayloadExceeded(firstLength)){ self.error('Max payload exceeded in compressed text message. Aborting...', 1009); return; } opcodes['2'].getData.call(self, firstLength); } else if (firstLength == 126) { self.expectHeader(2, function(data) { var length = readUInt16BE.call(data, 0); if (self.maxPayloadExceeded(length)){ self.error('Max payload exceeded in compressed text message. Aborting...', 1009); return; } opcodes['2'].getData.call(self, length); }); } else if (firstLength == 127) { self.expectHeader(8, function(data) { if (readUInt32BE.call(data, 0) != 0) { self.error('packets with length spanning more than 32 bit is currently not supported', 1008); return; } var length = readUInt32BE.call(data, 4, true); if (self.maxPayloadExceeded(length)){ self.error('Max payload exceeded in compressed text message. Aborting...', 1009); return; } opcodes['2'].getData.call(self, length); }); } }, getData: function(length) { var self = this; if (self.state.masked) { self.expectHeader(4, function(data) { var mask = data; self.expectData(length, function(data) { opcodes['2'].finish.call(self, mask, data); }); }); } else { self.expectData(length, function(data) { opcodes['2'].finish.call(self, null, data); }); } }, finish: function(mask, data) { var self = this; var packet = this.unmask(mask, data, true) || new Buffer(0); var state = clone(this.state); this.messageHandlers.push(function(callback) { self.applyExtensions(packet, state.lastFragment, state.compressed, function(err, buffer) { if (err) { if(err.type===1009){ return self.error('Max payload exceeded in compressed binary message. Aborting...', 1009); } return self.error(err.message, 1007); } if (buffer != null) { if( self.maxPayload==0 || (self.maxPayload > 0 && (self.currentMessageLength + buffer.length) < self.maxPayload) ){ self.currentMessage.push(buffer); } else{ self.currentMessage=null; self.currentMessage = []; self.currentMessageLength = 0; self.error(new Error('Maximum payload exceeded'), 1009); return; } self.currentMessageLength += buffer.length; } if (state.lastFragment) { var messageBuffer = Buffer.concat(self.currentMessage); self.currentMessage = []; self.currentMessageLength = 0; self.onbinary(messageBuffer, {masked: state.masked, buffer: messageBuffer}); } callback(); }); }); this.flush(); this.endPacket(); } }, // close '8': { start: function(data) { var self = this; if (self.state.lastFragment == false) { self.error('fragmented close is not supported', 1002); return; } // decode length var firstLength = data[1] & 0x7f; if (firstLength < 126) { opcodes['8'].getData.call(self, firstLength); } else { self.error('control frames cannot have more than 125 bytes of data', 1002); } }, getData: function(length) { var self = this; if (self.state.masked) { self.expectHeader(4, function(data) { var mask = data; self.expectData(length, function(data) { opcodes['8'].finish.call(self, mask, data); }); }); } else { self.expectData(length, function(data) { opcodes['8'].finish.call(self, null, data); }); } }, finish: function(mask, data) { var self = this; data = self.unmask(mask, data, true); var state = clone(this.state); this.messageHandlers.push(function() { if (data && data.length == 1) { self.error('close packets with data must be at least two bytes long', 1002); return; } var code = data && data.length > 1 ? readUInt16BE.call(data, 0) : 1000; if (!ErrorCodes.isValidErrorCode(code)) { self.error('invalid error code', 1002); return; } var message = ''; if (data && data.length > 2) { var messageBuffer = data.slice(2); if (!Validation.isValidUTF8(messageBuffer)) { self.error('invalid utf8 sequence', 1007); return; } message = messageBuffer.toString('utf8'); } self.onclose(code, message, {masked: state.masked}); self.reset(); }); this.flush(); }, }, // ping '9': { start: function(data) { var self = this; if (self.state.lastFragment == false) { self.error('fragmented ping is not supported', 1002); return; } // decode length var firstLength = data[1] & 0x7f; if (firstLength < 126) { opcodes['9'].getData.call(self, firstLength); } else { self.error('control frames cannot have more than 125 bytes of data', 1002); } }, getData: function(length) { var self = this; if (self.state.masked) { self.expectHeader(4, function(data) { var mask = data; self.expectData(length, function(data) { opcodes['9'].finish.call(self, mask, data); }); }); } else { self.expectData(length, function(data) { opcodes['9'].finish.call(self, null, data); }); } }, finish: function(mask, data) { var self = this; data = this.unmask(mask, data, true); var state = clone(this.state); this.messageHandlers.push(function(callback) { self.onping(data, {masked: state.masked, binary: true}); callback(); }); this.flush(); this.endPacket(); } }, // pong '10': { start: function(data) { var self = this; if (self.state.lastFragment == false) { self.error('fragmented pong is not supported', 1002); return; } // decode length var firstLength = data[1] & 0x7f; if (firstLength < 126) { opcodes['10'].getData.call(self, firstLength); } else { self.error('control frames cannot have more than 125 bytes of data', 1002); } }, getData: function(length) { var self = this; if (this.state.masked) { this.expectHeader(4, function(data) { var mask = data; self.expectData(length, function(data) { opcodes['10'].finish.call(self, mask, data); }); }); } else { this.expectData(length, function(data) { opcodes['10'].finish.call(self, null, data); }); } }, finish: function(mask, data) { var self = this; data = self.unmask(mask, data, true); var state = clone(this.state); this.messageHandlers.push(function(callback) { self.onpong(data, {masked: state.masked, binary: true}); callback(); }); this.flush(); this.endPacket(); } } } /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ var events = require('events') , util = require('util') , EventEmitter = events.EventEmitter; /** * Hixie Sender implementation */ function Sender(socket) { if (this instanceof Sender === false) { throw new TypeError("Classes can't be function-called"); } events.EventEmitter.call(this); this.socket = socket; this.continuationFrame = false; this.isClosed = false; } module.exports = Sender; /** * Inherits from EventEmitter. */ util.inherits(Sender, events.EventEmitter); /** * Frames and writes data. * * @api public */ Sender.prototype.send = function(data, options, cb) { if (this.isClosed) return; var isString = typeof data == 'string' , length = isString ? Buffer.byteLength(data) : data.length , lengthbytes = (length > 127) ? 2 : 1 // assume less than 2**14 bytes , writeStartMarker = this.continuationFrame == false , writeEndMarker = !options || !(typeof options.fin != 'undefined' && !options.fin) , buffer = new Buffer((writeStartMarker ? ((options && options.binary) ? (1 + lengthbytes) : 1) : 0) + length + ((writeEndMarker && !(options && options.binary)) ? 1 : 0)) , offset = writeStartMarker ? 1 : 0; if (writeStartMarker) { if (options && options.binary) { buffer.write('\x80', 'binary'); // assume length less than 2**14 bytes if (lengthbytes > 1) buffer.write(String.fromCharCode(128+length/128), offset++, 'binary'); buffer.write(String.fromCharCode(length&0x7f), offset++, 'binary'); } else buffer.write('\x00', 'binary'); } if (isString) buffer.write(data, offset, 'utf8'); else data.copy(buffer, offset, 0); if (writeEndMarker) { if (options && options.binary) { // sending binary, not writing end marker } else buffer.write('\xff', offset + length, 'binary'); this.continuationFrame = false; } else this.continuationFrame = true; try { this.socket.write(buffer, 'binary', cb); } catch (e) { this.error(e.toString()); } }; /** * Sends a close instruction to the remote party. * * @api public */ Sender.prototype.close = function(code, data, mask, cb) { if (this.isClosed) return; this.isClosed = true; try { if (this.continuationFrame) this.socket.write(new Buffer([0xff], 'binary')); this.socket.write(new Buffer([0xff, 0x00]), 'binary', cb); } catch (e) { this.error(e.toString()); } }; /** * Sends a ping message to the remote party. Not available for hixie. * * @api public */ Sender.prototype.ping = function(data, options) {}; /** * Sends a pong message to the remote party. Not available for hixie. * * @api public */ Sender.prototype.pong = function(data, options) {}; /** * Handles an error * * @api private */ Sender.prototype.error = function (reason) { this.emit('error', reason); return this; }; /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ var events = require('events') , util = require('util') , EventEmitter = events.EventEmitter , ErrorCodes = require('./ErrorCodes') , bufferUtil = require('./BufferUtil').BufferUtil , PerMessageDeflate = require('./PerMessageDeflate'); /** * HyBi Sender implementation */ function Sender(socket, extensions) { if (this instanceof Sender === false) { throw new TypeError("Classes can't be function-called"); } events.EventEmitter.call(this); this._socket = socket; this.extensions = extensions || {}; this.firstFragment = true; this.compress = false; this.messageHandlers = []; this.processing = false; } /** * Inherits from EventEmitter. */ util.inherits(Sender, events.EventEmitter); /** * Sends a close instruction to the remote party. * * @api public */ Sender.prototype.close = function(code, data, mask, cb) { if (typeof code !== 'undefined') { if (typeof code !== 'number' || !ErrorCodes.isValidErrorCode(code)) throw new Error('first argument must be a valid error code number'); } code = code || 1000; var dataBuffer = new Buffer(2 + (data ? Buffer.byteLength(data) : 0)); writeUInt16BE.call(dataBuffer, code, 0); if (dataBuffer.length > 2) dataBuffer.write(data, 2); var self = this; this.messageHandlers.push(function(callback) { self.frameAndSend(0x8, dataBuffer, true, mask); callback(); if (typeof cb == 'function') cb(); }); this.flush(); }; /** * Sends a ping message to the remote party. * * @api public */ Sender.prototype.ping = function(data, options) { var mask = options && options.mask; var self = this; this.messageHandlers.push(function(callback) { self.frameAndSend(0x9, data || '', true, mask); callback(); }); this.flush(); }; /** * Sends a pong message to the remote party. * * @api public */ Sender.prototype.pong = function(data, options) { var mask = options && options.mask; var self = this; this.messageHandlers.push(function(callback) { self.frameAndSend(0xa, data || '', true, mask); callback(); }); this.flush(); }; /** * Sends text or binary data to the remote party. * * @api public */ Sender.prototype.send = function(data, options, cb) { var finalFragment = options && options.fin === false ? false : true; var mask = options && options.mask; var compress = options && options.compress; var opcode = options && options.binary ? 2 : 1; if (this.firstFragment === false) { opcode = 0; compress = false; } else { this.firstFragment = false; this.compress = compress; } if (finalFragment) this.firstFragment = true var compressFragment = this.compress; var self = this; this.messageHandlers.push(function(callback) { self.applyExtensions(data, finalFragment, compressFragment, function(err, data) { if (err) { if (typeof cb == 'function') cb(err); else self.emit('error', err); return; } self.frameAndSend(opcode, data, finalFragment, mask, compress, cb); callback(); }); }); this.flush(); }; /** * Frames and sends a piece of data according to the HyBi WebSocket protocol. * * @api private */ Sender.prototype.frameAndSend = function(opcode, data, finalFragment, maskData, compressed, cb) { var canModifyData = false; if (!data) { try { this._socket.write(new Buffer([opcode | (finalFragment ? 0x80 : 0), 0 | (maskData ? 0x80 : 0)].concat(maskData ? [0, 0, 0, 0] : [])), 'binary', cb); } catch (e) { if (typeof cb == 'function') cb(e); else this.emit('error', e); } return; } if (!Buffer.isBuffer(data)) { canModifyData = true; if (data && (typeof data.byteLength !== 'undefined' || typeof data.buffer !== 'undefined')) { data = getArrayBuffer(data); } else { // // If people want to send a number, this would allocate the number in // bytes as memory size instead of storing the number as buffer value. So // we need to transform it to string in order to prevent possible // vulnerabilities / memory attacks. // if (typeof data === 'number') data = data.toString(); data = new Buffer(data); } } var dataLength = data.length , dataOffset = maskData ? 6 : 2 , secondByte = dataLength; if (dataLength >= 65536) { dataOffset += 8; secondByte = 127; } else if (dataLength > 125) { dataOffset += 2; secondByte = 126; } var mergeBuffers = dataLength < 32768 || (maskData && !canModifyData); var totalLength = mergeBuffers ? dataLength + dataOffset : dataOffset; var outputBuffer = new Buffer(totalLength); outputBuffer[0] = finalFragment ? opcode | 0x80 : opcode; if (compressed) outputBuffer[0] |= 0x40; switch (secondByte) { case 126: writeUInt16BE.call(outputBuffer, dataLength, 2); break; case 127: writeUInt32BE.call(outputBuffer, 0, 2); writeUInt32BE.call(outputBuffer, dataLength, 6); } if (maskData) { outputBuffer[1] = secondByte | 0x80; var mask = getRandomMask(); outputBuffer[dataOffset - 4] = mask[0]; outputBuffer[dataOffset - 3] = mask[1]; outputBuffer[dataOffset - 2] = mask[2]; outputBuffer[dataOffset - 1] = mask[3]; if (mergeBuffers) { bufferUtil.mask(data, mask, outputBuffer, dataOffset, dataLength); try { this._socket.write(outputBuffer, 'binary', cb); } catch (e) { if (typeof cb == 'function') cb(e); else this.emit('error', e); } } else { bufferUtil.mask(data, mask, data, 0, dataLength); try { this._socket.write(outputBuffer, 'binary'); this._socket.write(data, 'binary', cb); } catch (e) { if (typeof cb == 'function') cb(e); else this.emit('error', e); } } } else { outputBuffer[1] = secondByte; if (mergeBuffers) { data.copy(outputBuffer, dataOffset); try { this._socket.write(outputBuffer, 'binary', cb); } catch (e) { if (typeof cb == 'function') cb(e); else this.emit('error', e); } } else { try { this._socket.write(outputBuffer, 'binary'); this._socket.write(data, 'binary', cb); } catch (e) { if (typeof cb == 'function') cb(e); else this.emit('error', e); } } } }; /** * Execute message handler buffers * * @api private */ Sender.prototype.flush = function() { if (this.processing) return; var handler = this.messageHandlers.shift(); if (!handler) return; this.processing = true; var self = this; handler(function() { self.processing = false; self.flush(); }); }; /** * Apply extensions to message * * @api private */ Sender.prototype.applyExtensions = function(data, fin, compress, callback) { if (compress && data) { if ((data.buffer || data) instanceof ArrayBuffer) { data = getArrayBuffer(data); } this.extensions[PerMessageDeflate.extensionName].compress(data, fin, callback); } else { callback(null, data); } }; module.exports = Sender; function writeUInt16BE(value, offset) { this[offset] = (value & 0xff00)>>8; this[offset+1] = value & 0xff; } function writeUInt32BE(value, offset) { this[offset] = (value & 0xff000000)>>24; this[offset+1] = (value & 0xff0000)>>16; this[offset+2] = (value & 0xff00)>>8; this[offset+3] = value & 0xff; } function getArrayBuffer(data) { // data is either an ArrayBuffer or ArrayBufferView. var array = new Uint8Array(data.buffer || data) , l = data.byteLength || data.length , o = data.byteOffset || 0 , buffer = new Buffer(l); for (var i = 0; i < l; ++i) { buffer[i] = array[o+i]; } return buffer; } function getRandomMask() { return new Buffer([ ~~(Math.random() * 255), ~~(Math.random() * 255), ~~(Math.random() * 255), ~~(Math.random() * 255) ]); } /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ exports.Validation = { isValidUTF8: function(buffer) { return true; } }; 'use strict'; /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ try { module.exports = require('utf-8-validate'); } catch (e) { module.exports = require('./Validation.fallback'); } 'use strict'; /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ var url = require('url') , util = require('util') , http = require('http') , https = require('https') , crypto = require('crypto') , stream = require('stream') , Ultron = require('ultron') , Options = require('options') , Sender = require('./Sender') , Receiver = require('./Receiver') , SenderHixie = require('./Sender.hixie') , ReceiverHixie = require('./Receiver.hixie') , Extensions = require('./Extensions') , PerMessageDeflate = require('./PerMessageDeflate') , EventEmitter = require('events').EventEmitter; /** * Constants */ // Default protocol version var protocolVersion = 13; // Close timeout var closeTimeout = 30 * 1000; // Allow 30 seconds to terminate the connection cleanly /** * WebSocket implementation * * @constructor * @param {String} address Connection address. * @param {String|Array} protocols WebSocket protocols. * @param {Object} options Additional connection options. * @api public */ function WebSocket(address, protocols, options) { if (this instanceof WebSocket === false) { return new WebSocket(address, protocols, options); } EventEmitter.call(this); if (protocols && !Array.isArray(protocols) && 'object' === typeof protocols) { // accept the "options" Object as the 2nd argument options = protocols; protocols = null; } if ('string' === typeof protocols) { protocols = [ protocols ]; } if (!Array.isArray(protocols)) { protocols = []; } this._socket = null; this._ultron = null; this._closeReceived = false; this.bytesReceived = 0; this.readyState = null; this.supports = {}; this.extensions = {}; this._binaryType = 'nodebuffer'; if (Array.isArray(address)) { initAsServerClient.apply(this, address.concat(options)); } else { initAsClient.apply(this, [address, protocols, options]); } } /** * Inherits from EventEmitter. */ util.inherits(WebSocket, EventEmitter); /** * Ready States */ ["CONNECTING", "OPEN", "CLOSING", "CLOSED"].forEach(function each(state, index) { WebSocket.prototype[state] = WebSocket[state] = index; }); /** * Gracefully closes the connection, after sending a description message to the server * * @param {Object} data to be sent to the server * @api public */ WebSocket.prototype.close = function close(code, data) { if (this.readyState === WebSocket.CLOSED) return; if (this.readyState === WebSocket.CONNECTING) { this.readyState = WebSocket.CLOSED; return; } if (this.readyState === WebSocket.CLOSING) { if (this._closeReceived && this._isServer) { this.terminate(); } return; } var self = this; try { this.readyState = WebSocket.CLOSING; this._closeCode = code; this._closeMessage = data; var mask = !this._isServer; this._sender.close(code, data, mask, function(err) { if (err) self.emit('error', err); if (self._closeReceived && self._isServer) { self.terminate(); } else { // ensure that the connection is cleaned up even when no response of closing handshake. clearTimeout(self._closeTimer); self._closeTimer = setTimeout(cleanupWebsocketResources.bind(self, true), closeTimeout); } }); } catch (e) { this.emit('error', e); } }; /** * Pause the client stream * * @api public */ WebSocket.prototype.pause = function pauser() { if (this.readyState !== WebSocket.OPEN) throw new Error('not opened'); return this._socket.pause(); }; /** * Sends a ping * * @param {Object} data to be sent to the server * @param {Object} Members - mask: boolean, binary: boolean * @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open * @api public */ WebSocket.prototype.ping = function ping(data, options, dontFailWhenClosed) { if (this.readyState !== WebSocket.OPEN) { if (dontFailWhenClosed === true) return; throw new Error('not opened'); } options = options || {}; if (typeof options.mask === 'undefined') options.mask = !this._isServer; this._sender.ping(data, options); }; /** * Sends a pong * * @param {Object} data to be sent to the server * @param {Object} Members - mask: boolean, binary: boolean * @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open * @api public */ WebSocket.prototype.pong = function(data, options, dontFailWhenClosed) { if (this.readyState !== WebSocket.OPEN) { if (dontFailWhenClosed === true) return; throw new Error('not opened'); } options = options || {}; if (typeof options.mask === 'undefined') options.mask = !this._isServer; this._sender.pong(data, options); }; /** * Resume the client stream * * @api public */ WebSocket.prototype.resume = function resume() { if (this.readyState !== WebSocket.OPEN) throw new Error('not opened'); return this._socket.resume(); }; /** * Sends a piece of data * * @param {Object} data to be sent to the server * @param {Object} Members - mask: boolean, binary: boolean, compress: boolean * @param {function} Optional callback which is executed after the send completes * @api public */ WebSocket.prototype.send = function send(data, options, cb) { if (typeof options === 'function') { cb = options; options = {}; } if (this.readyState !== WebSocket.OPEN) { if (typeof cb === 'function') cb(new Error('not opened')); else throw new Error('not opened'); return; } if (!data) data = ''; if (this._queue) { var self = this; this._queue.push(function() { self.send(data, options, cb); }); return; } options = options || {}; options.fin = true; if (typeof options.binary === 'undefined') { options.binary = (data instanceof ArrayBuffer || data instanceof Buffer || data instanceof Uint8Array || data instanceof Uint16Array || data instanceof Uint32Array || data instanceof Int8Array || data instanceof Int16Array || data instanceof Int32Array || data instanceof Float32Array || data instanceof Float64Array); } if (typeof options.mask === 'undefined') options.mask = !this._isServer; if (typeof options.compress === 'undefined') options.compress = true; if (!this.extensions[PerMessageDeflate.extensionName]) { options.compress = false; } var readable = typeof stream.Readable === 'function' ? stream.Readable : stream.Stream; if (data instanceof readable) { startQueue(this); var self = this; sendStream(this, data, options, function send(error) { process.nextTick(function tock() { executeQueueSends(self); }); if (typeof cb === 'function') cb(error); }); } else { this._sender.send(data, options, cb); } }; /** * Streams data through calls to a user supplied function * * @param {Object} Members - mask: boolean, binary: boolean, compress: boolean * @param {function} 'function (error, send)' which is executed on successive ticks of which send is 'function (data, final)'. * @api public */ WebSocket.prototype.stream = function stream(options, cb) { if (typeof options === 'function') { cb = options; options = {}; } var self = this; if (typeof cb !== 'function') throw new Error('callback must be provided'); if (this.readyState !== WebSocket.OPEN) { if (typeof cb === 'function') cb(new Error('not opened')); else throw new Error('not opened'); return; } if (this._queue) { this._queue.push(function () { self.stream(options, cb); }); return; } options = options || {}; if (typeof options.mask === 'undefined') options.mask = !this._isServer; if (typeof options.compress === 'undefined') options.compress = true; if (!this.extensions[PerMessageDeflate.extensionName]) { options.compress = false; } startQueue(this); function send(data, final) { try { if (self.readyState !== WebSocket.OPEN) throw new Error('not opened'); options.fin = final === true; self._sender.send(data, options); if (!final) process.nextTick(cb.bind(null, null, send)); else executeQueueSends(self); } catch (e) { if (typeof cb === 'function') cb(e); else { delete self._queue; self.emit('error', e); } } } process.nextTick(cb.bind(null, null, send)); }; /** * Immediately shuts down the connection * * @api public */ WebSocket.prototype.terminate = function terminate() { if (this.readyState === WebSocket.CLOSED) return; if (this._socket) { this.readyState = WebSocket.CLOSING; // End the connection try { this._socket.end(); } catch (e) { // Socket error during end() call, so just destroy it right now cleanupWebsocketResources.call(this, true); return; } // Add a timeout to ensure that the connection is completely // cleaned up within 30 seconds, even if the clean close procedure // fails for whatever reason // First cleanup any pre-existing timeout from an earlier "terminate" call, // if one exists. Otherwise terminate calls in quick succession will leak timeouts // and hold the program open for `closeTimout` time. if (this._closeTimer) { clearTimeout(this._closeTimer); } this._closeTimer = setTimeout(cleanupWebsocketResources.bind(this, true), closeTimeout); } else if (this.readyState === WebSocket.CONNECTING) { cleanupWebsocketResources.call(this, true); } }; /** * Expose bufferedAmount * * @api public */ Object.defineProperty(WebSocket.prototype, 'bufferedAmount', { get: function get() { var amount = 0; if (this._socket) { amount = this._socket.bufferSize || 0; } return amount; } }); /** * Expose binaryType * * This deviates from the W3C interface since ws doesn't support the required * default "blob" type (instead we define a custom "nodebuffer" type). * * @see http://dev.w3.org/html5/websockets/#the-websocket-interface * @api public */ Object.defineProperty(WebSocket.prototype, 'binaryType', { get: function get() { return this._binaryType; }, set: function set(type) { if (type === 'arraybuffer' || type === 'nodebuffer') this._binaryType = type; else throw new SyntaxError('unsupported binaryType: must be either "nodebuffer" or "arraybuffer"'); } }); /** * Emulates the W3C Browser based WebSocket interface using function members. * * @see http://dev.w3.org/html5/websockets/#the-websocket-interface * @api public */ ['open', 'error', 'close', 'message'].forEach(function(method) { Object.defineProperty(WebSocket.prototype, 'on' + method, { /** * Returns the current listener * * @returns {Mixed} the set function or undefined * @api public */ get: function get() { var listener = this.listeners(method)[0]; return listener ? (listener._listener ? listener._listener : listener) : undefined; }, /** * Start listening for events * * @param {Function} listener the listener * @returns {Mixed} the set function or undefined * @api public */ set: function set(listener) { this.removeAllListeners(method); this.addEventListener(method, listener); } }); }); /** * Emulates the W3C Browser based WebSocket interface using addEventListener. * * @see https://developer.mozilla.org/en/DOM/element.addEventListener * @see http://dev.w3.org/html5/websockets/#the-websocket-interface * @api public */ WebSocket.prototype.addEventListener = function(method, listener) { var target = this; function onMessage (data, flags) { if (flags.binary && this.binaryType === 'arraybuffer') data = new Uint8Array(data).buffer; listener.call(target, new MessageEvent(data, !!flags.binary, target)); } function onClose (code, message) { listener.call(target, new CloseEvent(code, message, target)); } function onError (event) { event.type = 'error'; event.target = target; listener.call(target, event); } function onOpen () { listener.call(target, new OpenEvent(target)); } if (typeof listener === 'function') { if (method === 'message') { // store a reference so we can return the original function from the // addEventListener hook onMessage._listener = listener; this.on(method, onMessage); } else if (method === 'close') { // store a reference so we can return the original function from the // addEventListener hook onClose._listener = listener; this.on(method, onClose); } else if (method === 'error') { // store a reference so we can return the original function from the // addEventListener hook onError._listener = listener; this.on(method, onError); } else if (method === 'open') { // store a reference so we can return the original function from the // addEventListener hook onOpen._listener = listener; this.on(method, onOpen); } else { this.on(method, listener); } } }; module.exports = WebSocket; module.exports.buildHostHeader = buildHostHeader /** * W3C MessageEvent * * @see http://www.w3.org/TR/html5/comms.html * @constructor * @api private */ function MessageEvent(dataArg, isBinary, target) { this.type = 'message'; this.data = dataArg; this.target = target; this.binary = isBinary; // non-standard. } /** * W3C CloseEvent * * @see http://www.w3.org/TR/html5/comms.html * @constructor * @api private */ function CloseEvent(code, reason, target) { this.type = 'close'; this.wasClean = (typeof code === 'undefined' || code === 1000); this.code = code; this.reason = reason; this.target = target; } /** * W3C OpenEvent * * @see http://www.w3.org/TR/html5/comms.html * @constructor * @api private */ function OpenEvent(target) { this.type = 'open'; this.target = target; } // Append port number to Host header, only if specified in the url // and non-default function buildHostHeader(isSecure, hostname, port) { var headerHost = hostname; if (hostname) { if ((isSecure && (port != 443)) || (!isSecure && (port != 80))){ headerHost = headerHost + ':' + port; } } return headerHost; } /** * Entirely private apis, * which may or may not be bound to a sepcific WebSocket instance. */ function initAsServerClient(req, socket, upgradeHead, options) { options = new Options({ protocolVersion: protocolVersion, protocol: null, extensions: {}, maxPayload: 0 }).merge(options); // expose state properties this.protocol = options.value.protocol; this.protocolVersion = options.value.protocolVersion; this.extensions = options.value.extensions; this.supports.binary = (this.protocolVersion !== 'hixie-76'); this.upgradeReq = req; this.readyState = WebSocket.CONNECTING; this._isServer = true; this.maxPayload = options.value.maxPayload; // establish connection if (options.value.protocolVersion === 'hixie-76') { establishConnection.call(this, ReceiverHixie, SenderHixie, socket, upgradeHead); } else { establishConnection.call(this, Receiver, Sender, socket, upgradeHead); } } function initAsClient(address, protocols, options) { options = new Options({ origin: null, protocolVersion: protocolVersion, host: null, headers: null, protocol: protocols.join(','), agent: null, // ssl-related options pfx: null, key: null, passphrase: null, cert: null, ca: null, ciphers: null, rejectUnauthorized: null, perMessageDeflate: true, localAddress: null }).merge(options); if (options.value.protocolVersion !== 8 && options.value.protocolVersion !== 13) { throw new Error('unsupported protocol version'); } // verify URL and establish http class var serverUrl = url.parse(address); var isUnixSocket = serverUrl.protocol === 'ws+unix:'; if (!serverUrl.host && !isUnixSocket) throw new Error('invalid url'); var isSecure = serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:'; var httpObj = isSecure ? https : http; var port = serverUrl.port || (isSecure ? 443 : 80); var auth = serverUrl.auth; // prepare extensions var extensionsOffer = {}; var perMessageDeflate; if (options.value.perMessageDeflate) { perMessageDeflate = new PerMessageDeflate(typeof options.value.perMessageDeflate !== true ? options.value.perMessageDeflate : {}, false); extensionsOffer[PerMessageDeflate.extensionName] = perMessageDeflate.offer(); } // expose state properties this._isServer = false; this.url = address; this.protocolVersion = options.value.protocolVersion; this.supports.binary = (this.protocolVersion !== 'hixie-76'); // begin handshake var key = new Buffer(options.value.protocolVersion + '-' + Date.now()).toString('base64'); var shasum = crypto.createHash('sha1'); shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'); var expectedServerKey = shasum.digest('base64'); var agent = options.value.agent; var headerHost = buildHostHeader(isSecure, serverUrl.hostname, port) var requestOptions = { port: port, host: serverUrl.hostname, headers: { 'Connection': 'Upgrade', 'Upgrade': 'websocket', 'Host': headerHost, 'Sec-WebSocket-Version': options.value.protocolVersion, 'Sec-WebSocket-Key': key } }; // If we have basic auth. if (auth) { requestOptions.headers.Authorization = 'Basic ' + new Buffer(auth).toString('base64'); } if (options.value.protocol) { requestOptions.headers['Sec-WebSocket-Protocol'] = options.value.protocol; } if (options.value.host) { requestOptions.headers.Host = options.value.host; } if (options.value.headers) { for (var header in options.value.headers) { if (options.value.headers.hasOwnProperty(header)) { requestOptions.headers[header] = options.value.headers[header]; } } } if (Object.keys(extensionsOffer).length) { requestOptions.headers['Sec-WebSocket-Extensions'] = Extensions.format(extensionsOffer); } if (options.isDefinedAndNonNull('pfx') || options.isDefinedAndNonNull('key') || options.isDefinedAndNonNull('passphrase') || options.isDefinedAndNonNull('cert') || options.isDefinedAndNonNull('ca') || options.isDefinedAndNonNull('ciphers') || options.isDefinedAndNonNull('rejectUnauthorized')) { if (options.isDefinedAndNonNull('pfx')) requestOptions.pfx = options.value.pfx; if (options.isDefinedAndNonNull('key')) requestOptions.key = options.value.key; if (options.isDefinedAndNonNull('passphrase')) requestOptions.passphrase = options.value.passphrase; if (options.isDefinedAndNonNull('cert')) requestOptions.cert = options.value.cert; if (options.isDefinedAndNonNull('ca')) requestOptions.ca = options.value.ca; if (options.isDefinedAndNonNull('ciphers')) requestOptions.ciphers = options.value.ciphers; if (options.isDefinedAndNonNull('rejectUnauthorized')) requestOptions.rejectUnauthorized = options.value.rejectUnauthorized; if (!agent) { // global agent ignores client side certificates agent = new httpObj.Agent(requestOptions); } } requestOptions.path = serverUrl.path || '/'; if (agent) { requestOptions.agent = agent; } if (isUnixSocket) { requestOptions.socketPath = serverUrl.pathname; } if (options.value.localAddress) { requestOptions.localAddress = options.value.localAddress; } if (options.value.origin) { if (options.value.protocolVersion < 13) requestOptions.headers['Sec-WebSocket-Origin'] = options.value.origin; else requestOptions.headers.Origin = options.value.origin; } var self = this; var req = httpObj.request(requestOptions); req.on('error', function onerror(error) { self.emit('error', error); cleanupWebsocketResources.call(self, error); }); req.once('response', function response(res) { var error; if (!self.emit('unexpected-response', req, res)) { error = new Error('unexpected server response (' + res.statusCode + ')'); req.abort(); self.emit('error', error); } cleanupWebsocketResources.call(self, error); }); req.once('upgrade', function upgrade(res, socket, upgradeHead) { if (self.readyState === WebSocket.CLOSED) { // client closed before server accepted connection self.emit('close'); self.removeAllListeners(); socket.end(); return; } var serverKey = res.headers['sec-websocket-accept']; if (typeof serverKey === 'undefined' || serverKey !== expectedServerKey) { self.emit('error', 'invalid server key'); self.removeAllListeners(); socket.end(); return; } var serverProt = res.headers['sec-websocket-protocol']; var protList = (options.value.protocol || "").split(/, */); var protError = null; if (!options.value.protocol && serverProt) { protError = 'server sent a subprotocol even though none requested'; } else if (options.value.protocol && !serverProt) { protError = 'server sent no subprotocol even though requested'; } else if (serverProt && protList.indexOf(serverProt) === -1) { protError = 'server responded with an invalid protocol'; } if (protError) { self.emit('error', protError); self.removeAllListeners(); socket.end(); return; } else if (serverProt) { self.protocol = serverProt; } var serverExtensions = Extensions.parse(res.headers['sec-websocket-extensions']); if (perMessageDeflate && serverExtensions[PerMessageDeflate.extensionName]) { try { perMessageDeflate.accept(serverExtensions[PerMessageDeflate.extensionName]); } catch (err) { self.emit('error', 'invalid extension parameter'); self.removeAllListeners(); socket.end(); return; } self.extensions[PerMessageDeflate.extensionName] = perMessageDeflate; } establishConnection.call(self, Receiver, Sender, socket, upgradeHead); // perform cleanup on http resources req.removeAllListeners(); req = null; agent = null; }); req.end(); this.readyState = WebSocket.CONNECTING; } function establishConnection(ReceiverClass, SenderClass, socket, upgradeHead) { var ultron = this._ultron = new Ultron(socket) , called = false , self = this; socket.setTimeout(0); socket.setNoDelay(true); this._receiver = new ReceiverClass(this.extensions,this.maxPayload); this._socket = socket; // socket cleanup handlers ultron.on('end', cleanupWebsocketResources.bind(this)); ultron.on('close', cleanupWebsocketResources.bind(this)); ultron.on('error', cleanupWebsocketResources.bind(this)); // ensure that the upgradeHead is added to the receiver function firstHandler(data) { if (called || self.readyState === WebSocket.CLOSED) return; called = true; socket.removeListener('data', firstHandler); ultron.on('data', realHandler); if (upgradeHead && upgradeHead.length > 0) { realHandler(upgradeHead); upgradeHead = null; } if (data) realHandler(data); } // subsequent packets are pushed straight to the receiver function realHandler(data) { self.bytesReceived += data.length; self._receiver.add(data); } ultron.on('data', firstHandler); // if data was passed along with the http upgrade, // this will schedule a push of that on to the receiver. // this has to be done on next tick, since the caller // hasn't had a chance to set event handlers on this client // object yet. process.nextTick(firstHandler); // receiver event handlers self._receiver.ontext = function ontext(data, flags) { flags = flags || {}; self.emit('message', data, flags); }; self._receiver.onbinary = function onbinary(data, flags) { flags = flags || {}; flags.binary = true; self.emit('message', data, flags); }; self._receiver.onping = function onping(data, flags) { flags = flags || {}; self.pong(data, { mask: !self._isServer, binary: flags.binary === true }, true); self.emit('ping', data, flags); }; self._receiver.onpong = function onpong(data, flags) { self.emit('pong', data, flags || {}); }; self._receiver.onclose = function onclose(code, data, flags) { flags = flags || {}; self._closeReceived = true; self.close(code, data); }; self._receiver.onerror = function onerror(reason, errorCode) { // close the connection when the receiver reports a HyBi error code self.close(typeof errorCode !== 'undefined' ? errorCode : 1002, ''); self.emit('error', (reason instanceof Error) ? reason : (new Error(reason))); }; // finalize the client this._sender = new SenderClass(socket, this.extensions); this._sender.on('error', function onerror(error) { self.close(1002, ''); self.emit('error', error); }); this.readyState = WebSocket.OPEN; this.emit('open'); } function startQueue(instance) { instance._queue = instance._queue || []; } function executeQueueSends(instance) { var queue = instance._queue; if (typeof queue === 'undefined') return; delete instance._queue; for (var i = 0, l = queue.length; i < l; ++i) { queue[i](); } } function sendStream(instance, stream, options, cb) { stream.on('data', function incoming(data) { if (instance.readyState !== WebSocket.OPEN) { if (typeof cb === 'function') cb(new Error('not opened')); else { delete instance._queue; instance.emit('error', new Error('not opened')); } return; } options.fin = false; instance._sender.send(data, options); }); stream.on('end', function end() { if (instance.readyState !== WebSocket.OPEN) { if (typeof cb === 'function') cb(new Error('not opened')); else { delete instance._queue; instance.emit('error', new Error('not opened')); } return; } options.fin = true; instance._sender.send(null, options); if (typeof cb === 'function') cb(null); }); } function cleanupWebsocketResources(error) { if (this.readyState === WebSocket.CLOSED) return; this.readyState = WebSocket.CLOSED; clearTimeout(this._closeTimer); this._closeTimer = null; // If the connection was closed abnormally (with an error), or if // the close control frame was not received then the close code // must default to 1006. if (error || !this._closeReceived) { this._closeCode = 1006; } this.emit('close', this._closeCode || 1000, this._closeMessage || ''); if (this._socket) { if (this._ultron) this._ultron.destroy(); this._socket.on('error', function onerror() { try { this.destroy(); } catch (e) {} }); try { if (!error) this._socket.end(); else this._socket.destroy(); } catch (e) { /* Ignore termination errors */ } this._socket = null; this._ultron = null; } if (this._sender) { this._sender.removeAllListeners(); this._sender = null; } if (this._receiver) { this._receiver.cleanup(); this._receiver = null; } if (this.extensions[PerMessageDeflate.extensionName]) { this.extensions[PerMessageDeflate.extensionName].cleanup(); } this.extensions = null; this.removeAllListeners(); this.on('error', function onerror() {}); // catch all errors after this delete this._queue; } /*! * ws: a node.js websocket client * Copyright(c) 2011 Einar Otto Stangvik * MIT Licensed */ var util = require('util') , events = require('events') , http = require('http') , crypto = require('crypto') , Options = require('options') , WebSocket = require('./WebSocket') , Extensions = require('./Extensions') , PerMessageDeflate = require('./PerMessageDeflate') , tls = require('tls') , url = require('url'); /** * WebSocket Server implementation */ function WebSocketServer(options, callback) { if (this instanceof WebSocketServer === false) { return new WebSocketServer(options, callback); } events.EventEmitter.call(this); options = new Options({ host: '0.0.0.0', port: null, server: null, verifyClient: null, handleProtocols: null, path: null, noServer: false, disableHixie: false, clientTracking: true, perMessageDeflate: true, maxPayload: 100 * 1024 * 1024 }).merge(options); if (!options.isDefinedAndNonNull('port') && !options.isDefinedAndNonNull('server') && !options.value.noServer) { throw new TypeError('`port` or a `server` must be provided'); } var self = this; if (options.isDefinedAndNonNull('port')) { this._server = http.createServer(function (req, res) { var body = http.STATUS_CODES[426]; res.writeHead(426, { 'Content-Length': body.length, 'Content-Type': 'text/plain' }); res.end(body); }); this._server.allowHalfOpen = false; this._server.listen(options.value.port, options.value.host, callback); this._closeServer = function() { if (self._server) self._server.close(); }; } else if (options.value.server) { this._server = options.value.server; if (options.value.path) { // take note of the path, to avoid collisions when multiple websocket servers are // listening on the same http server if (this._server._webSocketPaths && options.value.server._webSocketPaths[options.value.path]) { throw new Error('two instances of WebSocketServer cannot listen on the same http server path'); } if (typeof this._server._webSocketPaths !== 'object') { this._server._webSocketPaths = {}; } this._server._webSocketPaths[options.value.path] = 1; } } if (this._server) { this._onceServerListening = function() { self.emit('listening'); }; this._server.once('listening', this._onceServerListening); } if (typeof this._server != 'undefined') { this._onServerError = function(error) { self.emit('error', error) }; this._server.on('error', this._onServerError); this._onServerUpgrade = function(req, socket, upgradeHead) { //copy upgradeHead to avoid retention of large slab buffers used in node core var head = new Buffer(upgradeHead.length); upgradeHead.copy(head); self.handleUpgrade(req, socket, head, function(client) { self.emit('connection'+req.url, client); self.emit('connection', client); }); }; this._server.on('upgrade', this._onServerUpgrade); } this.options = options.value; this.path = options.value.path; this.clients = []; } /** * Inherits from EventEmitter. */ util.inherits(WebSocketServer, events.EventEmitter); /** * Immediately shuts down the connection. * * @api public */ WebSocketServer.prototype.close = function(callback) { // terminate all associated clients var error = null; try { for (var i = 0, l = this.clients.length; i < l; ++i) { this.clients[i].terminate(); } } catch (e) { error = e; } // remove path descriptor, if any if (this.path && this._server._webSocketPaths) { delete this._server._webSocketPaths[this.path]; if (Object.keys(this._server._webSocketPaths).length == 0) { delete this._server._webSocketPaths; } } // close the http server if it was internally created try { if (typeof this._closeServer !== 'undefined') { this._closeServer(); } } finally { if (this._server) { this._server.removeListener('listening', this._onceServerListening); this._server.removeListener('error', this._onServerError); this._server.removeListener('upgrade', this._onServerUpgrade); } delete this._server; } if(callback) callback(error); else if(error) throw error; } /** * Handle a HTTP Upgrade request. * * @api public */ WebSocketServer.prototype.handleUpgrade = function(req, socket, upgradeHead, cb) { // check for wrong path if (this.options.path) { var u = url.parse(req.url); if (u && u.pathname !== this.options.path) return; } if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') { abortConnection(socket, 400, 'Bad Request'); return; } if (req.headers['sec-websocket-key1']) handleHixieUpgrade.apply(this, arguments); else handleHybiUpgrade.apply(this, arguments); } module.exports = WebSocketServer; /** * Entirely private apis, * which may or may not be bound to a sepcific WebSocket instance. */ function handleHybiUpgrade(req, socket, upgradeHead, cb) { // handle premature socket errors var errorHandler = function() { try { socket.destroy(); } catch (e) {} } socket.on('error', errorHandler); // verify key presence if (!req.headers['sec-websocket-key']) { abortConnection(socket, 400, 'Bad Request'); return; } // verify version var version = parseInt(req.headers['sec-websocket-version']); if ([8, 13].indexOf(version) === -1) { abortConnection(socket, 400, 'Bad Request'); return; } // verify protocol var protocols = req.headers['sec-websocket-protocol']; // verify client var origin = version < 13 ? req.headers['sec-websocket-origin'] : req.headers['origin']; // handle extensions offer var extensionsOffer = Extensions.parse(req.headers['sec-websocket-extensions']); // handler to call when the connection sequence completes var self = this; var completeHybiUpgrade2 = function(protocol) { // calc key var key = req.headers['sec-websocket-key']; var shasum = crypto.createHash('sha1'); shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); key = shasum.digest('base64'); var headers = [ 'HTTP/1.1 101 Switching Protocols' , 'Upgrade: websocket' , 'Connection: Upgrade' , 'Sec-WebSocket-Accept: ' + key ]; if (typeof protocol != 'undefined') { headers.push('Sec-WebSocket-Protocol: ' + protocol); } var extensions = {}; try { extensions = acceptExtensions.call(self, extensionsOffer); } catch (err) { abortConnection(socket, 400, 'Bad Request'); return; } if (Object.keys(extensions).length) { var serverExtensions = {}; Object.keys(extensions).forEach(function(token) { serverExtensions[token] = [extensions[token].params] }); headers.push('Sec-WebSocket-Extensions: ' + Extensions.format(serverExtensions)); } // allows external modification/inspection of handshake headers self.emit('headers', headers); socket.setTimeout(0); socket.setNoDelay(true); try { socket.write(headers.concat('', '').join('\r\n')); } catch (e) { // if the upgrade write fails, shut the connection down hard try { socket.destroy(); } catch (e) {} return; } var client = new WebSocket([req, socket, upgradeHead], { protocolVersion: version, protocol: protocol, extensions: extensions, maxPayload: self.options.maxPayload }); if (self.options.clientTracking) { self.clients.push(client); client.on('close', function() { var index = self.clients.indexOf(client); if (index != -1) { self.clients.splice(index, 1); } }); } // signal upgrade complete socket.removeListener('error', errorHandler); cb(client); } // optionally call external protocol selection handler before // calling completeHybiUpgrade2 var completeHybiUpgrade1 = function() { // choose from the sub-protocols if (typeof self.options.handleProtocols == 'function') { var protList = (protocols || "").split(/, */); var callbackCalled = false; var res = self.options.handleProtocols(protList, function(result, protocol) { callbackCalled = true; if (!result) abortConnection(socket, 401, 'Unauthorized'); else completeHybiUpgrade2(protocol); }); if (!callbackCalled) { // the handleProtocols handler never called our callback abortConnection(socket, 501, 'Could not process protocols'); } return; } else { if (typeof protocols !== 'undefined') { completeHybiUpgrade2(protocols.split(/, */)[0]); } else { completeHybiUpgrade2(); } } } // optionally call external client verification handler if (typeof this.options.verifyClient == 'function') { var info = { origin: origin, secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined', req: req }; if (this.options.verifyClient.length == 2) { this.options.verifyClient(info, function(result, code, name) { if (typeof code === 'undefined') code = 401; if (typeof name === 'undefined') name = http.STATUS_CODES[code]; if (!result) abortConnection(socket, code, name); else completeHybiUpgrade1(); }); return; } else if (!this.options.verifyClient(info)) { abortConnection(socket, 401, 'Unauthorized'); return; } } completeHybiUpgrade1(); } function handleHixieUpgrade(req, socket, upgradeHead, cb) { // handle premature socket errors var errorHandler = function() { try { socket.destroy(); } catch (e) {} } socket.on('error', errorHandler); // bail if options prevent hixie if (this.options.disableHixie) { abortConnection(socket, 401, 'Hixie support disabled'); return; } // verify key presence if (!req.headers['sec-websocket-key2']) { abortConnection(socket, 400, 'Bad Request'); return; } var origin = req.headers['origin'] , self = this; // setup handshake completion to run after client has been verified var onClientVerified = function() { var wshost; if (!req.headers['x-forwarded-host']) wshost = req.headers.host; else wshost = req.headers['x-forwarded-host']; var location = ((req.headers['x-forwarded-proto'] === 'https' || socket.encrypted) ? 'wss' : 'ws') + '://' + wshost + req.url , protocol = req.headers['sec-websocket-protocol']; // build the response header and return a Buffer var buildResponseHeader = function() { var headers = [ 'HTTP/1.1 101 Switching Protocols' , 'Upgrade: WebSocket' , 'Connection: Upgrade' , 'Sec-WebSocket-Location: ' + location ]; if (typeof protocol != 'undefined') headers.push('Sec-WebSocket-Protocol: ' + protocol); if (typeof origin != 'undefined') headers.push('Sec-WebSocket-Origin: ' + origin); return new Buffer(headers.concat('', '').join('\r\n')); }; // send handshake response before receiving the nonce var handshakeResponse = function() { socket.setTimeout(0); socket.setNoDelay(true); var headerBuffer = buildResponseHeader(); try { socket.write(headerBuffer, 'binary', function(err) { // remove listener if there was an error if (err) socket.removeListener('data', handler); return; }); } catch (e) { try { socket.destroy(); } catch (e) {} return; }; }; // handshake completion code to run once nonce has been successfully retrieved var completeHandshake = function(nonce, rest, headerBuffer) { // calculate key var k1 = req.headers['sec-websocket-key1'] , k2 = req.headers['sec-websocket-key2'] , md5 = crypto.createHash('md5'); [k1, k2].forEach(function (k) { var n = parseInt(k.replace(/[^\d]/g, '')) , spaces = k.replace(/[^ ]/g, '').length; if (spaces === 0 || n % spaces !== 0){ abortConnection(socket, 400, 'Bad Request'); return; } n /= spaces; md5.update(String.fromCharCode( n >> 24 & 0xFF, n >> 16 & 0xFF, n >> 8 & 0xFF, n & 0xFF)); }); md5.update(nonce.toString('binary')); socket.setTimeout(0); socket.setNoDelay(true); try { var hashBuffer = new Buffer(md5.digest('binary'), 'binary'); var handshakeBuffer = new Buffer(headerBuffer.length + hashBuffer.length); headerBuffer.copy(handshakeBuffer, 0); hashBuffer.copy(handshakeBuffer, headerBuffer.length); // do a single write, which - upon success - causes a new client websocket to be setup socket.write(handshakeBuffer, 'binary', function(err) { if (err) return; // do not create client if an error happens var client = new WebSocket([req, socket, rest], { protocolVersion: 'hixie-76', protocol: protocol }); if (self.options.clientTracking) { self.clients.push(client); client.on('close', function() { var index = self.clients.indexOf(client); if (index != -1) { self.clients.splice(index, 1); } }); } // signal upgrade complete socket.removeListener('error', errorHandler); cb(client); }); } catch (e) { try { socket.destroy(); } catch (e) {} return; } } // retrieve nonce var nonceLength = 8; if (upgradeHead && upgradeHead.length >= nonceLength) { var nonce = upgradeHead.slice(0, nonceLength); var rest = upgradeHead.length > nonceLength ? upgradeHead.slice(nonceLength) : null; completeHandshake.call(self, nonce, rest, buildResponseHeader()); } else { // nonce not present in upgradeHead var nonce = new Buffer(nonceLength); upgradeHead.copy(nonce, 0); var received = upgradeHead.length; var rest = null; var handler = function (data) { var toRead = Math.min(data.length, nonceLength - received); if (toRead === 0) return; data.copy(nonce, received, 0, toRead); received += toRead; if (received == nonceLength) { socket.removeListener('data', handler); if (toRead < data.length) rest = data.slice(toRead); // complete the handshake but send empty buffer for headers since they have already been sent completeHandshake.call(self, nonce, rest, new Buffer(0)); } } // handle additional data as we receive it socket.on('data', handler); // send header response before we have the nonce to fix haproxy buffering handshakeResponse(); } } // verify client if (typeof this.options.verifyClient == 'function') { var info = { origin: origin, secure: typeof req.connection.authorized !== 'undefined' || typeof req.connection.encrypted !== 'undefined', req: req }; if (this.options.verifyClient.length == 2) { var self = this; this.options.verifyClient(info, function(result, code, name) { if (typeof code === 'undefined') code = 401; if (typeof name === 'undefined') name = http.STATUS_CODES[code]; if (!result) abortConnection(socket, code, name); else onClientVerified.apply(self); }); return; } else if (!this.options.verifyClient(info)) { abortConnection(socket, 401, 'Unauthorized'); return; } } // no client verification required onClientVerified(); } function acceptExtensions(offer) { var extensions = {}; var options = this.options.perMessageDeflate; var maxPayload = this.options.maxPayload; if (options && offer[PerMessageDeflate.extensionName]) { var perMessageDeflate = new PerMessageDeflate(options !== true ? options : {}, true, maxPayload); perMessageDeflate.accept(offer[PerMessageDeflate.extensionName]); extensions[PerMessageDeflate.extensionName] = perMessageDeflate; } return extensions; } function abortConnection(socket, code, name) { try { var response = [ 'HTTP/1.1 ' + code + ' ' + name, 'Content-type: text/html' ]; socket.write(response.concat('', '').join('\r\n')); } catch (e) { /* ignore errors - we've aborted this connection */ } finally { // ensure that an early aborted connection is shut down completely try { socket.destroy(); } catch (e) {} } } ALL_TESTS = $(shell find test/ -name '*.test.js') ALL_INTEGRATION = $(shell find test/ -name '*.integration.js') run-tests: @./node_modules/.bin/mocha \ -t 5000 \ -s 2400 \ $(TESTFLAGS) \ $(TESTS) run-integrationtests: @./node_modules/.bin/mocha \ -t 5000 \ -s 6000 \ $(TESTFLAGS) \ $(TESTS) run-coverage: @./node_modules/.bin/istanbul cover --report html \ ./node_modules/.bin/_mocha -- \ -t 5000 \ -s 6000 \ $(TESTFLAGS) \ $(TESTS) test: @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests integrationtest: @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_INTEGRATION)" run-integrationtests coverage: @$(MAKE) NODE_TLS_REJECT_UNAUTHORIZED=0 NODE_PATH=lib TESTS="$(ALL_TESTS)" run-coverage benchmark: @node bench/sender.benchmark.js @node bench/parser.benchmark.js autobahn: @NODE_PATH=lib node test/autobahn.js autobahn-server: @NODE_PATH=lib node test/autobahn-server.js .PHONY: test coverage 'use strict'; var has = Object.prototype.hasOwnProperty; /** * An auto incrementing id which we can use to create "unique" Ultron instances * so we can track the event emitters that are added through the Ultron * interface. * * @type {Number} * @private */ var id = 0; /** * Ultron is high-intelligence robot. It gathers intelligence so it can start improving * upon his rudimentary design. It will learn from your EventEmitting patterns * and exterminate them. * * @constructor * @param {EventEmitter} ee EventEmitter instance we need to wrap. * @api public */ function Ultron(ee) { if (!(this instanceof Ultron)) return new Ultron(ee); this.id = id++; this.ee = ee; } /** * Register a new EventListener for the given event. * * @param {String} event Name of the event. * @param {Functon} fn Callback function. * @param {Mixed} context The context of the function. * @returns {Ultron} * @api public */ Ultron.prototype.on = function on(event, fn, context) { fn.__ultron = this.id; this.ee.on(event, fn, context); return this; }; /** * Add an EventListener that's only called once. * * @param {String} event Name of the event. * @param {Function} fn Callback function. * @param {Mixed} context The context of the function. * @returns {Ultron} * @api public */ Ultron.prototype.once = function once(event, fn, context) { fn.__ultron = this.id; this.ee.once(event, fn, context); return this; }; /** * Remove the listeners we assigned for the given event. * * @returns {Ultron} * @api public */ Ultron.prototype.remove = function remove() { var args = arguments , event; // // When no event names are provided we assume that we need to clear all the // events that were assigned through us. // if (args.length === 1 && 'string' === typeof args[0]) { args = args[0].split(/[, ]+/); } else if (!args.length) { args = []; for (event in this.ee._events) { if (has.call(this.ee._events, event)) args.push(event); } } for (var i = 0; i < args.length; i++) { var listeners = this.ee.listeners(args[i]); for (var j = 0; j < listeners.length; j++) { event = listeners[j]; // // Once listeners have a `listener` property that stores the real listener // in the EventEmitter that ships with Node.js. // if (event.listener) { if (event.listener.__ultron !== this.id) continue; delete event.listener.__ultron; } else { if (event.__ultron !== this.id) continue; delete event.__ultron; } this.ee.removeListener(args[i], event); } } return this; }; /** * Destroy the Ultron instance, remove all listeners and release all references. * * @returns {Boolean} * @api public */ Ultron.prototype.destroy = function destroy() { if (!this.ee) return false; this.remove(); this.ee = null; return true; }; // // Expose the module. // module.exports = Ultron; The MIT License (MIT) Copyright (c) 2015 Unshift.io, Arnout Kazemier, the 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. { "_args": [ [ "ultron@1.0.2", "C:\\Users\\wenju\\Documents\\projects\\core" ] ], "_from": "ultron@1.0.2", "_id": "ultron@1.0.2", "_inBundle": false, "_integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=", "_location": "/ws/ultron", "_phantomChildren": {}, "_requested": { "type": "version", "registry": true, "raw": "ultron@1.0.2", "name": "ultron", "escapedName": "ultron", "rawSpec": "1.0.2", "saveSpec": null, "fetchSpec": "1.0.2" }, "_requiredBy": [ "/ws" ], "_resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", "_spec": "1.0.2", "_where": "C:\\Users\\wenju\\Documents\\projects\\core", "author": { "name": "Arnout Kazemier" }, "bugs": { "url": "https://github.com/unshiftio/ultron/issues" }, "description": "Ultron is high-intelligence robot. It gathers intel so it can start improving upon his rudimentary design", "devDependencies": { "assume": "1.2.x", "eventemitter3": "1.1.x", "istanbul": "0.3.x", "mocha": "2.2.x", "pre-commit": "1.0.x" }, "homepage": "https://github.com/unshiftio/ultron", "keywords": [ "Ultron", "robot", "gather", "intelligence", "event", "events", "eventemitter", "emitter", "cleanup" ], "license": "MIT", "main": "index.js", "name": "ultron", "repository": { "type": "git", "url": "git+https://github.com/unshiftio/ultron.git" }, "scripts": { "100%": "istanbul check-coverage --statements 100 --functions 100 --lines 100 --branches 100", "coverage": "istanbul cover ./node_modules/.bin/_mocha -- test.js", "test": "mocha test.js", "test-travis": "istanbul cover node_modules/.bin/_mocha --report lcovonly -- test.js", "watch": "mocha --watch test.js" }, "version": "1.0.2" } # Ultron [![Made by unshift](https://img.shields.io/badge/made%20by-unshift-00ffcc.svg?style=flat-square)](http://unshift.io)[![Version npm](http://img.shields.io/npm/v/ultron.svg?style=flat-square)](http://browsenpm.org/package/ultron)[![Build Status](http://img.shields.io/travis/unshiftio/ultron/master.svg?style=flat-square)](https://travis-ci.org/unshiftio/ultron)[![Dependencies](https://img.shields.io/david/unshiftio/ultron.svg?style=flat-square)](https://david-dm.org/unshiftio/ultron)[![Coverage Status](http://img.shields.io/coveralls/unshiftio/ultron/master.svg?style=flat-square)](https://coveralls.io/r/unshiftio/ultron?branch=master)[![IRC channel](http://img.shields.io/badge/IRC-irc.freenode.net%23unshift-00a8ff.svg?style=flat-square)](http://webchat.freenode.net/?channels=unshift) Ultron is a high-intelligence robot. It gathers intelligence so it can start improving upon his rudimentary design. It will learn your event emitting patterns and find ways to exterminate them. Allowing you to remove only the event emitters that **you** assigned and not the ones that your users or developers assigned. This can prevent race conditions, memory leaks and even file descriptor leaks from ever happening as you won't remove clean up processes. ## Installation The module is designed to be used in browsers using browserify and in Node.js. You can install the module through the public npm registry by running the following command in CLI: ``` npm install --save ultron ``` ## Usage In all examples we assume that you've required the library as following: ```js 'use strict'; var Ultron = require('ultron'); ``` Now that we've required the library we can construct our first `Ultron` instance. The constructor requires one argument which should be the `EventEmitter` instance that we need to operate upon. This can be the `EventEmitter` module that ships with Node.js or `EventEmitter3` or anything else as long as it follow the same API and internal structure as these 2. So with that in mind we can create the instance: ```js // // For the sake of this example we're going to construct an empty EventEmitter // var EventEmitter = require('events').EventEmitter; // or require('eventmitter3'); var events = new EventEmitter(); var ultron = new Ultron(events); ``` You can now use the following API's from the Ultron instance: ### Ultron.on Register a new event listener for the given event. It follows the exact same API as `EventEmitter.on` but it will return itself instead of returning the EventEmitter instance. If you are using EventEmitter3 it also supports the context param: ```js ultron.on('event-name', handler, { custom: 'function context' }); ``` ### Ultron.once Exactly the same as the [Ultron.on](#ultronon) but it only allows the execution once. ### Ultron.remove This is where all the magic happens and the safe removal starts. This function accepts different argument styles: - No arguments, assume that all events need to be removed so it will work as `removeAllListeners()` API. - 1 argument, when it's a string it will be split on ` ` and `,` to create a list of events that need to be cleared. - Multiple arguments, we assume that they are all names of events that need to be cleared. ```js ultron.remove('foo, bar baz'); // Removes foo, bar and baz. ultron.remove('foo', 'bar', 'baz'); // Removes foo, bar and baz. ultron.remove(); // Removes everything. ``` If you just want to remove a single event listener using a function reference you can still use the EventEmitter's `removeListener(event, fn)` API: ```js function foo() {} ulton.on('foo', foo); events.removeListener('foo', foo); ``` ## License MIT /* istanbul ignore next */ describe('Ultron', function () { 'use strict'; var EventEmitter = require('eventemitter3') , EE = require('events').EventEmitter , assume = require('assume') , Ultron = require('./') , ultron , ee; beforeEach(function () { ee = new EventEmitter(); ultron = new Ultron(ee); }); afterEach(function () { ultron.destroy(); ee.removeAllListeners(); }); it('is exposed as a function', function () { assume(Ultron).is.a('function'); }); it('can be initialized without the new keyword', function () { assume(Ultron(ee)).is.instanceOf(Ultron); }); it('assigns a unique id to every instance', function () { for (var i = 0; i < 100; i++) { assume(ultron.id).does.not.equal((new Ultron()).id); } }); it('allows removal through the event emitter', function () { function foo() {} function bar() {} ultron.on('foo', foo); ultron.once('foo', bar); assume(foo.__ultron).equals(ultron.id); assume(bar.__ultron).equals(ultron.id); assume(ee.listeners('foo').length).equals(2); ee.removeListener('foo', foo); assume(ee.listeners('foo').length).equals(1); ee.removeListener('foo', bar); assume(ee.listeners('foo').length).equals(0); }); describe('#on', function () { it('assigns a listener', function () { assume(ee.listeners('foo').length).equals(0); function foo() {} ultron.on('foo', foo); assume(ee.listeners('foo').length).equals(1); assume(ee.listeners('foo')[0]).equals(foo); }); it('tags the assigned function', function () { assume(ee.listeners('foo').length).equals(0); ultron.on('foo', function () {}); assume(ee.listeners('foo')[0].__ultron).equals(ultron.id); }); it('also passes in the context', function (next) { var context = 1313; ultron.on('foo', function (a, b, c) { assume(a).equals('a'); assume(b).equals('b'); assume(c).equals('c'); assume(this).equals(context); next(); }, context); ee.emit('foo', 'a', 'b', 'c'); }); it('works with regular eventemitters as well', function (next) { var ee = new EE() , ultron = new Ultron(ee); ultron.on('foo', function (a, b, c) { assume(a).equals('a'); assume(b).equals('b'); assume(c).equals('c'); next(); }); ee.emit('foo', 'a', 'b', 'c'); }); }); describe('#once', function () { it('assigns a listener', function () { assume(ee.listeners('foo').length).equals(0); function foo() {} ultron.once('foo', foo); assume(ee.listeners('foo').length).equals(1); assume(ee.listeners('foo')[0]).equals(foo); }); it('tags the assigned function', function () { assume(ee.listeners('foo').length).equals(0); ultron.once('foo', function () {}); assume(ee.listeners('foo')[0].__ultron).equals(ultron.id); }); it('also passes in the context', function (next) { var context = 1313; ultron.once('foo', function (a, b, c) { assume(a).equals('a'); assume(b).equals('b'); assume(c).equals('c'); assume(this).equals(context); next(); }, context); ee.emit('foo', 'a', 'b', 'c'); ee.emit('foo', 'a', 'b', 'c'); // Ensure that we don't double execute }); it('works with regular eventemitters as well', function (next) { var ee = new EE() , ultron = new Ultron(ee); ultron.once('foo', function (a, b, c) { assume(a).equals('a'); assume(b).equals('b'); assume(c).equals('c'); next(); }); ee.emit('foo', 'a', 'b', 'c'); ee.emit('foo', 'a', 'b', 'c'); // Ensure that we don't double execute }); }); describe('#remove', function () { it('removes only our assigned `on` listeners', function () { function foo() {} function bar() {} ee.on('foo', foo); ultron.on('foo', bar); assume(ee.listeners('foo').length).equals(2); ultron.remove('foo'); assume(ee.listeners('foo').length).equals(1); assume(ee.listeners('foo')[0]).equals(foo); }); it('removes our private __ultron references', function () { function once() {} function on() {} assume('__ultron' in once).is.false(); assume('__ultron' in on).is.false(); ultron.on('foo', on); ultron.once('bar', once); assume('__ultron' in once).is.true(); assume('__ultron' in on).is.true(); ultron.remove('foo, bar'); assume('__ultron' in once).is.false(); assume('__ultron' in on).is.false(); ultron.destroy(); ee = new EE(); ultron = new Ultron(ee); assume('__ultron' in once).is.false(); assume('__ultron' in on).is.false(); ultron.on('foo', on); ultron.once('bar', once); assume('__ultron' in once).is.true(); assume('__ultron' in on).is.true(); ultron.remove('foo, bar'); assume('__ultron' in once).is.false(); assume('__ultron' in on).is.false(); }); it('removes only our assigned `once` listeners', function () { function foo() {} function bar() {} ee.once('foo', foo); ultron.once('foo', bar); assume(ee.listeners('foo').length).equals(2); ultron.remove('foo'); assume(ee.listeners('foo').length).equals(1); assume(ee.listeners('foo')[0]).equals(foo); }); it('removes only our assigned `once` listeners from regular EE', function () { var ee = new EE() , ultron = new Ultron(ee); function foo() {} function bar() {} ee.once('foo', foo); ultron.once('foo', bar); assume(ee.listeners('foo').length).equals(2); ultron.remove('foo'); assume(ee.listeners('foo').length).equals(1); assume(ee.listeners('foo')[0].listener).equals(foo); }); it('removes all assigned events if called without args', function () { function foo() {} function bar() {} ultron.on('foo', foo); ultron.on('bar', bar); assume(ee.listeners('foo').length).equals(1); assume(ee.listeners('bar').length).equals(1); ultron.remove(); assume(ee.listeners('foo').length).equals(0); assume(ee.listeners('bar').length).equals(0); }); it('removes multiple listeners based on args', function () { function foo() {} function bar() {} function baz() {} ultron.on('foo', foo); ultron.on('bar', bar); ultron.on('baz', baz); assume(ee.listeners('foo').length).equals(1); assume(ee.listeners('bar').length).equals(1); assume(ee.listeners('baz').length).equals(1); ultron.remove('foo', 'bar'); assume(ee.listeners('foo').length).equals(0); assume(ee.listeners('bar').length).equals(0); assume(ee.listeners('baz').length).equals(1); }); it('removes multiple listeners if first arg is seperated string', function () { function foo() {} function bar() {} function baz() {} ultron.on('foo', foo); ultron.on('bar', bar); ultron.on('baz', baz); assume(ee.listeners('foo').length).equals(1); assume(ee.listeners('bar').length).equals(1); assume(ee.listeners('baz').length).equals(1); ultron.remove('foo, bar'); assume(ee.listeners('foo').length).equals(0); assume(ee.listeners('bar').length).equals(0); assume(ee.listeners('baz').length).equals(1); }); }); describe('#destroy', function () { it('removes all listeners', function () { function foo() {} function bar() {} function baz() {} ultron.on('foo', foo); ultron.on('bar', bar); ultron.on('baz', baz); assume(ee.listeners('foo').length).equals(1); assume(ee.listeners('bar').length).equals(1); assume(ee.listeners('baz').length).equals(1); ultron.destroy(); assume(ee.listeners('foo').length).equals(0); assume(ee.listeners('bar').length).equals(0); assume(ee.listeners('baz').length).equals(0); }); it('removes the .ee reference', function () { assume(ultron.ee).equals(ee); ultron.destroy(); assume(ultron.ee).equals(null); }); it('returns booleans for state indication', function () { assume(ultron.destroy()).is.true(); assume(ultron.destroy()).is.false(); assume(ultron.destroy()).is.false(); assume(ultron.destroy()).is.false(); }); }); }); { "_args": [ [ "ws@1.1.1", "C:\\Users\\wenju\\Documents\\projects\\core" ] ], "_from": "ws@1.1.1", "_id": "ws@1.1.1", "_inBundle": false, "_integrity": "sha1-CC3bbGQehdS7RR8D1S8G6r2x8Bg=", "_location": "/ws", "_phantomChildren": {}, "_requested": { "type": "version", "registry": true, "raw": "ws@1.1.1", "name": "ws", "escapedName": "ws", "rawSpec": "1.1.1", "saveSpec": null, "fetchSpec": "1.1.1" }, "_requiredBy": [ "/" ], "_resolved": "https://registry.npmjs.org/ws/-/ws-1.1.1.tgz", "_spec": "1.1.1", "_where": "C:\\Users\\wenju\\Documents\\projects\\core", "author": { "name": "Einar Otto Stangvik", "email": "einaros@gmail.com", "url": "http://2x.io" }, "bugs": { "url": "https://github.com/websockets/ws/issues" }, "dependencies": { "options": ">=0.0.5", "ultron": "1.0.x" }, "description": "simple to use, blazing fast and thoroughly tested websocket client, server and console for node.js, up-to-date against RFC-6455", "devDependencies": { "ansi": "0.3.x", "benchmark": "0.3.x", "bufferutil": "1.2.x", "expect.js": "0.3.x", "istanbul": "^0.4.1", "mocha": "2.3.x", "should": "8.0.x", "tinycolor": "0.0.x", "utf-8-validate": "1.2.x" }, "gypfile": true, "homepage": "https://github.com/websockets/ws#readme", "keywords": [ "Hixie", "HyBi", "Push", "RFC-6455", "WebSocket", "WebSockets", "real-time" ], "license": "MIT", "main": "index.js", "name": "ws", "repository": { "type": "git", "url": "git://github.com/websockets/ws.git" }, "scripts": { "test": "make test" }, "version": "1.1.1" } # ws: a node.js websocket library [![Build Status](https://travis-ci.org/websockets/ws.svg?branch=master)](https://travis-ci.org/websockets/ws) `ws` is a simple to use WebSocket implementation, up-to-date against RFC-6455, and [probably the fastest WebSocket library for node.js][archive]. Passes the quite extensive Autobahn test suite. See http://websockets.github.com/ws for the full reports. ## Protocol support * **Hixie draft 76** (Old and deprecated, but still in use by Safari and Opera. Added to ws version 0.4.2, but server only. Can be disabled by setting the `disableHixie` option to true.) * **HyBi drafts 07-12** (Use the option `protocolVersion: 8`) * **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`) ### Installing ``` npm install --save ws ``` ### Opt-in for performance There are 2 optional modules that can be installed along side with the `ws` module. These modules are binary addons which improve certain operations, but as they are binary addons they require compilation which can fail if no c++ compiler is installed on the host system. - `npm install --save bufferutil`: Improves internal buffer operations which allows for faster processing of masked WebSocket frames and general buffer operations. - `npm install --save utf-8-validate`: The specification requires validation of invalid UTF-8 chars, some of these validations could not be done in JavaScript hence the need for a binary addon. In most cases you will already be validating the input that you receive for security purposes leading to double validation. But if you want to be 100% spec-conforming and have fast validation of UTF-8 then this module is a must. ### Sending and receiving text data ```js var WebSocket = require('ws'); var ws = new WebSocket('ws://www.host.com/path'); ws.on('open', function open() { ws.send('something'); }); ws.on('message', function(data, flags) { // flags.binary will be set if a binary data is received. // flags.masked will be set if the data was masked. }); ``` ### Sending binary data ```js var WebSocket = require('ws'); var ws = new WebSocket('ws://www.host.com/path'); ws.on('open', function open() { var array = new Float32Array(5); for (var i = 0; i < array.length; ++i) { array[i] = i / 2; } ws.send(array, { binary: true, mask: true }); }); ``` Setting `mask`, as done for the send options above, will cause the data to be masked according to the WebSocket protocol. The same option applies for text data. ### Server example ```js var WebSocketServer = require('ws').Server , wss = new WebSocketServer({ port: 8080 }); wss.on('connection', function connection(ws) { ws.on('message', function incoming(message) { console.log('received: %s', message); }); ws.send('something'); }); ``` ### ExpressJS example ```js var server = require('http').createServer() , url = require('url') , WebSocketServer = require('ws').Server , wss = new WebSocketServer({ server: server }) , express = require('express') , app = express() , port = 4080; app.use(function (req, res) { res.send({ msg: "hello" }); }); wss.on('connection', function connection(ws) { var location = url.parse(ws.upgradeReq.url, true); // you might use location.query.access_token to authenticate or share sessions // or ws.upgradeReq.headers.cookie (see http://stackoverflow.com/a/16395220/151312) ws.on('message', function incoming(message) { console.log('received: %s', message); }); ws.send('something'); }); server.on('request', app); server.listen(port, function () { console.log('Listening on ' + server.address().port) }); ``` ### Server sending broadcast data ```js var WebSocketServer = require('ws').Server , wss = new WebSocketServer({ port: 8080 }); wss.broadcast = function broadcast(data) { wss.clients.forEach(function each(client) { client.send(data); }); }; ``` ### Error handling best practices ```js // If the WebSocket is closed before the following send is attempted ws.send('something'); // Errors (both immediate and async write errors) can be detected in an optional // callback. The callback is also the only way of being notified that data has // actually been sent. ws.send('something', function ack(error) { // if error is not defined, the send has been completed, // otherwise the error object will indicate what failed. }); // Immediate errors can also be handled with try/catch-blocks, but **note** that // since sends are inherently asynchronous, socket write failures will *not* be // captured when this technique is used. try { ws.send('something'); } catch (e) { /* handle error */ } ``` ### echo.websocket.org demo ```js var WebSocket = require('ws'); var ws = new WebSocket('ws://echo.websocket.org/', { protocolVersion: 8, origin: 'http://websocket.org' }); ws.on('open', function open() { console.log('connected'); ws.send(Date.now().toString(), {mask: true}); }); ws.on('close', function close() { console.log('disconnected'); }); ws.on('message', function message(data, flags) { console.log('Roundtrip time: ' + (Date.now() - parseInt(data)) + 'ms', flags); setTimeout(function timeout() { ws.send(Date.now().toString(), {mask: true}); }, 500); }); ``` ### Other examples For a full example with a browser client communicating with a ws server, see the examples folder. Note that the usage together with Express 3.0 is quite different from Express 2.x. The difference is expressed in the two different serverstats-examples. Otherwise, see the test cases. ### Running the tests ``` make test ``` ## API Docs See [`/doc/ws.md`](https://github.com/websockets/ws/blob/master/doc/ws.md) for Node.js-like docs for the ws classes. ## Changelog We're using the GitHub [`releases`](https://github.com/websockets/ws/releases) for changelog entries. ## License (The MIT License) Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.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. [archive]: http://web.archive.org/web/20130314230536/http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs # Security Guidelines Please contact us directly at **security@3rd-Eden.com** for any bug that might impact the security of this project. Please prefix the subject of your email with `[security]` in lowercase and square brackets. Our email filters will automatically prevent these messages from being moved to our spam box. You will receive an acknowledgement of your report within **24 hours**. All emails that do not include security vulnerabilities will be removed and blocked instantly. ## Exceptions If you do not receive an acknowledgement within the said time frame please give us the benefit of the doubt as it's possible that we haven't seen it yet. In this case please send us a message **without details** using one of the following methods: - Contact the lead developers of this project on their personal e-mails. You can find the e-mails in the git logs, for example using the following command: `git --no-pager show -s --format='%an <%ae>' ` where `` is the SHA1 of their latest commit in the project. - Create a GitHub issue stating contact details and the severity of the issue. Once we have acknowledged receipt of your report and confirmed the bug ourselves we will work with you to fix the vulnerability and publicly acknowledge your responsible disclosure, if you wish. In addition to that we will report all vulnerabilities to the [Node Security Project](https://nodesecurity.io/). ## History 04 Jan 2016: [Buffer vulnerablity](https://github.com/websockets/ws/releases/tag/1.0.1) { "name": "openfin", "version": "0.0.1", "description": "The core app of the OpenFin Runtime 8", "main": "index.js", "scripts": { "build": "grunt build-pac", "deploy": "grunt deploy", "postinstall": "grunt", "pretest": "grunt babel && grunt ts", "test": "grunt test" }, "repository": { "type": "git", "url": "https://github.com/openfin/openfin.git" }, "engines": { "node": ">=8.2.1" }, "author": "OpenFin", "license": "Apache-2.0", "bugs": { "url": "https://github.com/openfin/openfin/issues" }, "homepage": "https://github.com/openfin/openfin", "optionalDependencies": { "unix-dgram": "^2.0.2" }, "devDependencies": { "@types/minimist": "^1.2.0", "@types/mocha": "^5.2.5", "@types/mockery": "^1.4.29", "@types/node": "^10.5.2", "@types/rx": "^4.1.1", "@types/underscore": "^1.8.8", "asar": "^0.14.3", "babel-core": "^6.26.3", "babel-preset-es2015": "^6.24.1", "electron-rebuild": "^1.8.1", "grunt": "^1.0.3", "grunt-babel": "^7.0.0", "grunt-cli": "^1.2.0", "grunt-contrib-copy": "^1.0.0", "grunt-contrib-jshint": "^1.1.0", "grunt-contrib-watch": "^1.1.0", "grunt-jsbeautifier": "^0.2.13", "grunt-mocha-test": "^0.13.3", "grunt-ts": "^5.5.1", "grunt-tslint": "^5.0.2", "load-grunt-tasks": "^4.0.0", "mocha": "^5.2.0", "mockery": "^2.1.0", "openfin-sign": "openfin/openfin-sign.git#v1.0.0", "rewire": "^4.0.1", "should": "^13.2.1", "should-sinon": "0.0.6", "sinon": "^6.1.3", "tslint": "^5.11.0", "tslint-microsoft-contrib": "^5.0.3", "typescript": "^2.9.2", "wrench": "^1.5.9" }, "dependencies": { "minimist": "^1.2.0", "options": "0.0.6", "rx": "^4.1.0", "ultron": "^1.0.2", "underscore": "^1.8.3", "ws": "1.1.1" } } var PI_M2 = Math.PI * 2.0; var Tweens = { linear: function (t, b, c, d) { return d === 0 ? c + b : t * (c / d) + b; }, 'ease-out': function (t, b, c, d) { return d === 0 ? c + b : c * Math.sin(t / d * (Math.PI / 2)) + b; }, 'ease-in': function (t, b, c, d) { return d === 0 ? b + c : -c * Math.cos(t / d * (Math.PI / 2)) + c + b; }, 'ease-in-out': function (t, b, c, d) { return d === 0 ? b + c : -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; }, 'ease-in-quad': function (t, b, c, d) { t = d === 0 ? 1 : t / d; return c * t * t + b; }, 'ease-out-quad': function (t, b, c, d) { t = d === 0 ? 1 : t / d; return -c * t * (t - 2) + b; }, 'ease-in-out-quad': function (t, b, c, d) { t = d === 0 ? 2 : t / (d / 2); if (t < 1) { return c / 2 * t * t + b; } t--; return -c / 2 * (t * (t - 2) - 1) + b; }, 'ease-in-cubic': function (t, b, c, d) { t = t === 0 ? 1 : t / d; return c * t * t * t + b; }, 'ease-out-cubic': function (t, b, c, d) { t = t === 0 ? 1 : t / d; t--; return c * (t * t * t + 1) + b; }, 'ease-in-out-cubic': function (t, b, c, d) { t = d === 0 ? 2 : t / (d / 2); if (t < 1) { return c / 2 * t * t * t + b; } t -= 2; return c / 2 * (t * t * t + 2) + b; }, 'ease-out-bounce': function (t, b, c, d) { if (d === 0) { t = d = 1; } if ((t /= d) < (1 / 2.75)) { return c * (7.5625 * t * t) + b; } else if (t < (2 / 2.75)) { return c * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75) + b; } else if (t < (2.5 / 2.75)) { return c * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375) + b; } else { return c * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375) + b; } }, 'ease-out-back': function (t, b, c, d, s) { if (s === undefined) { s = 1.70158; } return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; }, 'ease-in-back': function (t, b, c, d, s) { if (d === 0) { t = d = 1; } if (s === undefined) { s = 1.70158; } return c * (t /= d) * t * ((s + 1) * t - s) + b; }, 'ease-in-out-back': function (t, b, c, d, s) { if (s === undefined) { s = 1.70158; } if (d === 0) { t = d = 1; } if ((t /= d / 2) < 1) { return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b; } return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b; }, 'ease-out-elastic': function (t, b, c, d, a, p) { var s; if (t === 0) { return b; } if (d === 0) { t = d = 1; } if ((t /= d) === 1) { return b + c; } if (!p) { p = d * 0.3; } if (!a || a < Math.abs(c)) { a = c; s = p / 4; } else { s = p / PI_M2 * Math.asin(c / a); } return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * PI_M2 / p) + c + b); }, 'ease-in-elastic': function (t, b, c, d, a, p) { var s; if (t === 0) { return b; } if (d === 0) { t = d = 1; } if ((t /= d) === 1) { return b + c; } if (!p) { p = d * 0.3; } if (!a || a < Math.abs(c)) { a = c; s = p / 4; } else { s = p / PI_M2 * Math.asin(c / a); } return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * PI_M2 / p)) + b; }, 'ease-in-out-elastic': function (t, b, c, d, a, p) { var s; if (t === 0) { return b; } if (d === 0) { t = d = 1; } if ((t /= d / 2) === 2) { return b + c; } if (!p) { p = d * (0.3 * 1.5); } if (!a || a < Math.abs(c)) { a = c; s = p / 4; } else { s = p / PI_M2 * Math.asin(c / a); } if (t < 1) { return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * PI_M2 / p)) + b; } return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * PI_M2 / p) * 0.5 + c + b; } }; module.exports = Tweens; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); let app = require('electron').app; let BrowserWindow = require('electron').BrowserWindow; let NativeTimer = require('electron').nativeTimer; let windowTransaction = require('electron').windowTransaction; const utils_1 = require("./utils"); let Deferred = require('./deferred.js'); let Tweens = require('./animation/tween.js'); const safe_int_1 = require("../common/safe_int"); const isWin32 = process.platform === 'win32'; let _screen; let _animationHandler; app.on('ready', function () { _screen = require('electron').screen; _animationHandler = new AnimationHandler(1000.0 / 40.0); }); function getAnimationHandler() { return _animationHandler; } function getScreen() { return _screen; } function AnimationHandler(desiredInterval) { var me = this, interval = Math.round(desiredInterval), _transitionsPerWindow = {}, _lastFrame, nativeTimer; var startTimerIfNeeded = () => { if (!nativeTimer.isRunning()) { _lastFrame = Date.now(); nativeTimer.reset(); } }; var stopTimer = () => { nativeTimer.stop(); }; var pump = (deltaTime, currentTime) => { var wt; let hwndToId = {}; var activeWindows = Object.keys(_transitionsPerWindow); const { flag: { noZorder, noActivate } } = windowTransaction; const flags = noZorder + noActivate; activeWindows.forEach((key) => { try { var bw = BrowserWindow.fromId(parseInt(key)); var currentWindowEntry = (_transitionsPerWindow[key] || {}); var transitions = currentWindowEntry.transitions || []; if (transitions.length) { var currentTransition = transitions[0]; if (!currentTransition.startTime) { bw.emit('synth-tween-start'); currentTransition.startTime = currentTime; var meta = currentTransition.transitions; var currentBounds = bw.getBounds(); if (meta.position) { let relative = meta.position.relative || false; let x = meta.position.left; let y = meta.position.top; meta.position.delta = { x: (typeof meta.position.left === 'number' ? (!relative ? x - currentBounds.x : x) : 0), y: (typeof meta.position.top === 'number' ? (!relative ? y - currentBounds.y : y) : 0) }; } if (meta.size) { let relative = meta.size.relative || false; let width = meta.size.width; let height = meta.size.height; meta.size.delta = { width: (typeof meta.size.width === 'number' ? (!relative ? width - currentBounds.width : width) : 0), height: (typeof meta.size.height === 'number' ? (!relative ? height - currentBounds.height : height) : 0) }; } if (meta.opacity) { let relative = meta.opacity.relative || false; let opacity = meta.opacity.opacity; meta.opacity.delta = { value: (typeof meta.opacity.opacity === 'number' ? (!relative ? opacity - bw.getOpacity() : opacity) : 0) }; } currentTransition.initialOpacity = bw.getOpacity(); currentTransition.initialBounds = bw.getBounds(); } var transitionDelta = currentTime - currentTransition.startTime; var tween = Tweens[currentTransition.transitionType] || function () { return 0; }; var totalDuration; var currentDuration; var updateBounds = false; var updateOpacity = false; var opacityChange = 0.0; var positionTransition = currentTransition.transitions.position; var sizeTransition = currentTransition.transitions.size; var opacityTransition = currentTransition.transitions.opacity; var boundsChange = { x: 0, y: 0, width: 0, height: 0 }; if (positionTransition) { totalDuration = positionTransition.duration || 0; currentDuration = Math.min(totalDuration, transitionDelta); if (positionTransition.delta) { updateBounds = true; boundsChange.x = tween(currentDuration, 0, positionTransition.delta.x, totalDuration); boundsChange.y = tween(currentDuration, 0, positionTransition.delta.y, totalDuration); } if (currentDuration >= totalDuration) { updateBounds = true; delete currentTransition.transitions.position; positionTransition = undefined; currentTransition.initialBounds.x = currentTransition.initialBounds.x + (boundsChange.x | 0); currentTransition.initialBounds.y = currentTransition.initialBounds.y + (boundsChange.y | 0); boundsChange.x = 0; boundsChange.y = 0; } } if (sizeTransition) { totalDuration = sizeTransition.duration || 0; currentDuration = Math.min(totalDuration, transitionDelta); if (sizeTransition.delta) { updateBounds = true; boundsChange.width = tween(currentDuration, 0, sizeTransition.delta.width, totalDuration); boundsChange.height = tween(currentDuration, 0, sizeTransition.delta.height, totalDuration); } if (currentDuration >= totalDuration) { updateBounds = true; delete currentTransition.transitions.size; sizeTransition = undefined; currentTransition.initialBounds.width = currentTransition.initialBounds.width + (boundsChange.width | 0); currentTransition.initialBounds.height = currentTransition.initialBounds.height + (boundsChange.height | 0); boundsChange.width = 0; boundsChange.height = 0; } } if (opacityTransition) { totalDuration = opacityTransition.duration || 0; currentDuration = Math.min(totalDuration, transitionDelta); if (opacityTransition.delta) { updateOpacity = true; opacityChange = tween(currentDuration, 0, opacityTransition.delta.value, totalDuration); } if (currentDuration >= totalDuration) { updateOpacity = true; delete currentTransition.transitions.opacity; opacityTransition = undefined; currentTransition.initialOpacity = currentTransition.initialOpacity + opacityChange; opacityChange = 0; } } if (updateOpacity) { currentWindowEntry.hadOpacityChange = true; bw.setOpacity(Math.min(Math.max(0, currentTransition.initialOpacity + opacityChange), 1.0)); } if (updateBounds) { currentWindowEntry.hadBoundsChange = true; let { x, y, width, height } = currentTransition.initialBounds; x = safe_int_1.toSafeInt(x + boundsChange.x, x); y = safe_int_1.toSafeInt(y + boundsChange.y, y); width = safe_int_1.toSafeInt(width + boundsChange.width, width); height = safe_int_1.toSafeInt(height + boundsChange.height, height); const newBounds = utils_1.clipBounds({ x, y, width, height }, bw); if (isWin32) { let hwnd = parseInt(bw.nativeId, 16); if (!wt) { wt = new windowTransaction.Transaction(0); wt.on('deferred-set-window-pos', (event, payload) => { payload.forEach((winPos) => { let bwId = hwndToId[parseInt(winPos.hwnd)]; Deferred.handleMove(bwId, winPos); }); }); } hwndToId[hwnd] = bw.id; if (bw.isMaximized()) { bw.unmaximize(); } const { x, y, width: w, height: h } = newBounds; wt.setWindowPos(hwnd, { x, y, w, h, flags }); } else { if (bw.isMaximized()) { bw.unmaximize(); } bw.setBounds(newBounds); } } if (!sizeTransition && !positionTransition && !opacityTransition) { transitions.splice(0, 1); currentTransition.resolve(); bw.emit('synth-tween-end'); } } else { bw.emit('synth-animate-end', { opacity: currentWindowEntry.hadOpacityChange, bounds: currentWindowEntry.hadBoundsChange }); delete _transitionsPerWindow[key]; } } catch (e) { } }); if (wt) { wt.commit(); } if (!activeWindows.length) { stopTimer(); } }; nativeTimer = new NativeTimer(() => { var currentFrame = Date.now(); try { pump(currentFrame - _lastFrame, currentFrame, _lastFrame); } catch (e) { } _lastFrame = Date.now(); }, interval); me.hasWindow = (id) => { return !!_transitionsPerWindow[id]; }; me.add = (browserWindow, meta, transitionType, successCallback, errorCallback) => { meta = meta || {}; _transitionsPerWindow[browserWindow.id] = _transitionsPerWindow[browserWindow.id] || { hadOpacityChange: false, hadBoundsChange: false, transitions: [] }; var entry = _transitionsPerWindow[browserWindow.id].transitions; var now = Date.now(); var maxDuration = Math.max((meta.size || {}).duration || 0, Math.max((meta.opacity || {}).duration || 0, (meta.position || {}).duration || 0)); if (meta.interrupt) { entry.forEach(function (transition) { transition.resolve(undefined, 'interrupted'); }); entry.length = 0; } entry.push({ startTime: undefined, maxDuration: maxDuration, transitions: meta, endTime: now + maxDuration, transitionType: transitionType, resolve: function (err) { if (!err && typeof successCallback === 'function') { successCallback(); } else if (err && typeof errorCallback === 'function') { errorCallback(err); } } }); startTimerIfNeeded(); }; return me; } module.exports = { getScreen, AnimationHandler, getAnimationHandler }; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const coreState = require('../../core_state'); const api_protocol_base_1 = require("./api_protocol_base"); const rvmBus = require('../../rvm/rvm_message_bus').rvmMessageBus; const log_1 = require("../../log"); const electron_1 = require("electron"); const configUrlPermissionsMap = {}; const CONFIG_URL_WILDCARD = 'default'; const ENABLE_DESKTOP_OWNER_SETTINGS = 'enable-desktop-owner-settings'; const DESKTOP_OWNER_SETTINGS_TIMEOUT = 'desktop-owner-settings-timeout'; let desktopOwnerSettingsTimeout = 2000; let desktopOwnerSettingEnabled = false; let apiPolicies; var POLICY_AUTH_RESULT; (function (POLICY_AUTH_RESULT) { POLICY_AUTH_RESULT[POLICY_AUTH_RESULT["Allowed"] = 1] = "Allowed"; POLICY_AUTH_RESULT[POLICY_AUTH_RESULT["Denied"] = 2] = "Denied"; POLICY_AUTH_RESULT[POLICY_AUTH_RESULT["NotDefined"] = 3] = "NotDefined"; })(POLICY_AUTH_RESULT || (POLICY_AUTH_RESULT = {})); const delegateMap = new Map(); function getApiPath(action) { return api_protocol_base_1.actionMap[action] ? api_protocol_base_1.actionMap[action].apiPath : ''; } function searchPolicyByConfigUrl(url) { log_1.writeToLog(1, `searchPolicyByConfigUrl ${url}`, true); if (apiPolicies) { for (const policyName of Object.keys(apiPolicies)) { const policy = apiPolicies[policyName]; if (Array.isArray(policy.urls) && electron_1.app.matchesURL(url, policy.urls)) { log_1.writeToLog(1, `searchPolicyByConfigUrl matched by policy name ${policyName}`, true); return policy; } else if (electron_1.app.matchesURL(url, [policyName])) { log_1.writeToLog(1, `searchPolicyByConfigUrl matched by policy name ${policyName}`, true); return policy; } } } else { log_1.writeToLog('error', 'searchPolicyByConfigUrl: missing API policies'); } } function checkWindowPermissions(apiPath, windowPermissions, isChildWindow, payload) { let permitted = isChildWindow; if (windowPermissions) { const parts = apiPath.split('.'); const levels = parts.length; let level; let lastValue = windowPermissions; for (level = 0; level < levels; level += 1) { const part = parts[level]; if (lastValue.hasOwnProperty(part)) { lastValue = lastValue[part]; } else { break; } } log_1.writeToLog(1, `checkWindowPermissions level ${level} ${apiPath}`, true); if (level === levels) { if (typeof lastValue === 'boolean') { permitted = lastValue; } else if (delegateMap.has(apiPath)) { log_1.writeToLog(1, `checkWindowPermissions calling delegate ${apiPath}`, true); permitted = delegateMap.get(apiPath).checkPermissions({ apiPath, permissionSettings: lastValue, payload }); } else { log_1.writeToLog(1, `checkWindowPermissions api path not defined ${apiPath}`, true); } } } return permitted; } function authorizeActionFromWindowOptions(windowOpts, parentUuid, action, payload) { windowOpts = windowOpts || {}; const { uuid, name, permissions } = windowOpts; const logSuffix = `'${action}' for ${uuid} ${name}`; log_1.writeToLog(1, `authorizeAction ${logSuffix}`, true); const apiPath = getApiPath(action); let allowed = true; if (apiPath) { const isChildWindow = uuid !== name; allowed = isChildWindow; if (permissions) { allowed = checkWindowPermissions(apiPath, permissions, isChildWindow, payload); } } if (allowed && parentUuid) { const parentObject = coreState.getAppObjByUuid(parentUuid); if (parentObject) { const parentOpts = parentObject._options; if (parentOpts) { log_1.writeToLog(1, `authorizeAction checks parent ${parentUuid} ${logSuffix}`, true); allowed = authorizeActionFromWindowOptions(parentOpts, parentObject.parentUuid, action, payload); return; } else { log_1.writeToLog(1, `authorizeAction missing parent options ${parentUuid} ${logSuffix}`, true); } } else { log_1.writeToLog(1, `authorizeAction missing parent ${parentUuid} ${logSuffix}`, true); } } return allowed; } function authorizeActionFromPolicy(windowOpts, action, payload) { const { uuid, name } = windowOpts; const logSuffix = `'${action}' for ${uuid} ${name}`; log_1.writeToLog(1, `authorizeActionFromPolicy ${logSuffix}`, true); const apiPath = getApiPath(action); return new Promise((resolve, reject) => { if (desktopOwnerSettingEnabled === true) { const configUrl = coreState.getConfigUrlByUuid(uuid); if (configUrl) { log_1.writeToLog(1, `authorizeActionFromPolicy checking with config url ${configUrl} ${logSuffix}`, true); requestAppPermissions(configUrl).then((resultByUrl) => { if (resultByUrl.permissions) { resolve(checkWindowPermissions(apiPath, resultByUrl.permissions, false, payload) ? POLICY_AUTH_RESULT.Allowed : POLICY_AUTH_RESULT.Denied); } else { log_1.writeToLog(1, `authorizeActionFromPolicy checking with RVM ${CONFIG_URL_WILDCARD} ${logSuffix}`, true); requestAppPermissions(CONFIG_URL_WILDCARD).then((resultByDefault) => { if (resultByDefault.permissions) { resolve(checkWindowPermissions(apiPath, resultByDefault.permissions, false, payload) ? POLICY_AUTH_RESULT.Allowed : POLICY_AUTH_RESULT.Denied); } else { resolve(POLICY_AUTH_RESULT.NotDefined); } }).catch((error) => { log_1.writeToLog(1, `authorizeActionFromPolicy query for permissions failed ${CONFIG_URL_WILDCARD} ${logSuffix} ${error}`, true); reject(false); }); } }).catch((error) => { log_1.writeToLog(1, `authorizeActionFromPolicy query for permissions failed ${configUrl} ${logSuffix} ${error}`, true); reject(false); }); } else { log_1.writeToLog(1, `authorizeActionFromPolicy configUrl not defined ${logSuffix}`, true); resolve(POLICY_AUTH_RESULT.NotDefined); } } else { log_1.writeToLog(1, `authorizeActionFromPolicy desktopOwnerSettingEnabled ${desktopOwnerSettingEnabled} ${logSuffix}`, true); resolve(POLICY_AUTH_RESULT.NotDefined); } }); } function apiPolicyPreProcessor(msg, next) { const { identity, data, nack } = msg; const { action, payload } = data; const apiPath = getApiPath(action); if (typeof identity === 'object' && apiPath) { const { uuid, name } = identity; const logSuffix = `'${action}' from ${uuid} ${name}`; log_1.writeToLog(1, `apiPolicyPreProcessor ${logSuffix}`, true); const originWindow = coreState.getWindowByUuidName(uuid, name); if (originWindow) { const appObject = coreState.getAppObjByUuid(uuid); const parentUuid = uuid === name ? appObject.parentUuid : uuid; authorizeActionFromPolicy(coreState.getWindowOptionsById(originWindow.id), action, payload). then((result) => { if (result === POLICY_AUTH_RESULT.Allowed) { next(); } else if (result === POLICY_AUTH_RESULT.Denied) { log_1.writeToLog(1, `apiPolicyPreProcessor rejecting from policy ${logSuffix}`, true); nack('Rejected, action is not authorized'); } else if (authorizeActionFromWindowOptions(coreState.getWindowOptionsById(originWindow.id), parentUuid, action, payload)) { next(); } else { log_1.writeToLog(1, `apiPolicyPreProcessor rejecting from win opts ${logSuffix}`, true); nack('Rejected, action is not authorized'); } }).catch(() => { log_1.writeToLog(1, `apiPolicyPreProcessor rejecting from error ${logSuffix}`, true); nack('Rejected, action is not authorized'); }); } else { log_1.writeToLog(1, `apiPolicyPreProcessor missing origin window ${logSuffix}`, true); next(); } } else { next(); } } exports.apiPolicyPreProcessor = apiPolicyPreProcessor; function requestAppPermissions(configUrl) { log_1.writeToLog(1, `requestAppPermissions ${configUrl}`, true); return new Promise((resolve, reject) => { if (configUrlPermissionsMap[configUrl]) { log_1.writeToLog(1, 'requestAppPermissions cached', true); resolve(configUrlPermissionsMap[configUrl]); } else { const policy = searchPolicyByConfigUrl(configUrl); if (policy && policy.permissions) { configUrlPermissionsMap[configUrl] = { permissions: policy.permissions }; } else { configUrlPermissionsMap[configUrl] = {}; } resolve(configUrlPermissionsMap[configUrl]); } }); } function registerDelegate(apiPath, delegate) { log_1.writeToLog(1, `register API policy delegate ${apiPath}`, true); delegateMap.set(apiPath, delegate); } function retrieveAPIPolicyContent() { log_1.writeToLog(1, 'retrieveAPIPolicyContent', true); return new Promise((resolve, reject) => { const msg = { topic: 'application', action: 'get-desktop-owner-settings', sourceUrl: 'https://openfin.co', isGlobal: true }; rvmBus.publish(msg, (rvmResponse) => { log_1.writeToLog('info', `requestAppPermissions from RVM ${JSON.stringify(rvmResponse)} `); if (rvmResponse.payload && rvmResponse.payload.success === true && rvmResponse.payload.payload) { resolve(rvmResponse.payload.payload); } else { log_1.writeToLog('error', `requestAppPermissions from RVM failed ${JSON.stringify(rvmResponse)}`); reject(rvmResponse); } }, desktopOwnerSettingsTimeout / 1000); }); } if (coreState.argo['enable-strict-api-permissions']) { log_1.writeToLog('info', `Installing API policy PreProcessor ${JSON.stringify(coreState.getStartManifest())}`); api_protocol_base_1.getDefaultRequestHandler().addPreProcessor(apiPolicyPreProcessor); desktopOwnerSettingEnabled = !!coreState.argo[ENABLE_DESKTOP_OWNER_SETTINGS]; log_1.writeToLog(1, `desktopOwnerSettingEnabled ${desktopOwnerSettingEnabled}`, true); if (desktopOwnerSettingEnabled === true && coreState.argo[DESKTOP_OWNER_SETTINGS_TIMEOUT]) { desktopOwnerSettingsTimeout = Number(coreState.argo[DESKTOP_OWNER_SETTINGS_TIMEOUT]); log_1.writeToLog(1, `desktopOwnerSettingsTimeout ${desktopOwnerSettingsTimeout}`, true); } for (const key of Object.keys(api_protocol_base_1.actionMap)) { const endPoint = api_protocol_base_1.actionMap[key]; if (endPoint.apiPolicyDelegate) { registerDelegate(endPoint.apiPath, endPoint.apiPolicyDelegate); } } if (desktopOwnerSettingEnabled === true) { retrieveAPIPolicyContent().then((content) => { apiPolicies = content; }).catch(e => { log_1.writeToLog(1, `Error retrieveAPIPolicies ${e}`, true); }); } } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const external_application_1 = require("../../api/external_application"); const subscription_manager_1 = require("../../subscription_manager"); const WebSocketStrategy = require('../transport_strategy/ws_strategy').WebSocketStrategy; const ElipcStrategy = require('../transport_strategy/elipc_strategy').ElipcStrategy; const base_handler_1 = require("../transport_strategy/base_handler"); exports.actionMap = Object.create(null); const requestHandler = new base_handler_1.default(); const webSocketStrategy = new WebSocketStrategy(exports.actionMap, requestHandler); const elipcStrategy = new ElipcStrategy(exports.actionMap, requestHandler); const subscriptionManager = new subscription_manager_1.default(); function registerActionMap(specs, authorizationPathPrefix) { Object.getOwnPropertyNames(specs).forEach((key) => { if (!exports.actionMap[key]) { const spec = specs[key]; let endpoint; if (typeof spec === 'function') { endpoint = { apiFunc: spec }; } else if (typeof spec === 'object') { endpoint = spec; } else { throw new Error(`Expected action spec to be function or object but found ${typeof spec}`); } if (typeof endpoint.apiFunc !== 'function') { throw new Error(`Expected endpoint.apiFunc to be function but found ${typeof endpoint.apiFunc}`); } let apiPath = endpoint.apiPath; if (apiPath) { if (apiPath[0] === '.') { if (!authorizationPathPrefix) { throw new Error('Needed authorization prefix not provided on this call to registerActionMap'); } apiPath = authorizationPathPrefix + apiPath; } else if (apiPath.indexOf('.') < 0) { throw new Error(`Expected "${apiPath}" to be an apiFunc (starts with dot) or an apiPath (contains dot)`); } endpoint.apiPath = apiPath; } exports.actionMap[key] = endpoint; } else { throw new Error(`Key collision "${key}" is already registered`); } }); } exports.registerActionMap = registerActionMap; function sendToIdentity(identity, payload) { const externalConnection = external_application_1.ExternalApplication.getExternalConnectionByUuid(identity.uuid); if (externalConnection) { webSocketStrategy.send(externalConnection, payload); } else { elipcStrategy.send(identity, payload); } } exports.sendToIdentity = sendToIdentity; function subscriptionExists(identity, ...args) { return subscriptionManager.subscriptionExists(identity, ...args); } exports.subscriptionExists = subscriptionExists; function uppSubscriptionRefCount(identity, ...args) { return subscriptionManager.uppSubscriptionRefCount(identity, ...args); } exports.uppSubscriptionRefCount = uppSubscriptionRefCount; function registerSubscription(fn, identity, ...args) { return subscriptionManager.registerSubscription(fn, identity, ...args); } exports.registerSubscription = registerSubscription; function removeSubscription(identity, ...args) { return subscriptionManager.removeSubscription(identity, ...args); } exports.removeSubscription = removeSubscription; function getDefaultRequestHandler() { return requestHandler; } exports.getDefaultRequestHandler = getDefaultRequestHandler; function getGroupingWindowIdentity(payload) { return { uuid: payload.groupingUuid, name: payload.groupingWindowName }; } exports.getGroupingWindowIdentity = getGroupingWindowIdentity; function getTargetWindowIdentity(payload) { return { uuid: payload.uuid, name: payload.name }; } exports.getTargetWindowIdentity = getTargetWindowIdentity; function getTargetApplicationIdentity(payload) { return { uuid: payload.uuid }; } exports.getTargetApplicationIdentity = getTargetApplicationIdentity; function onClientAuthenticated(cb) { webSocketStrategy.onClientAuthenticated(cb); } exports.onClientAuthenticated = onClientAuthenticated; function onClientDisconnect(id, cb) { webSocketStrategy.onClientDisconnect(onDisconnect(id, cb)); } exports.onClientDisconnect = onClientDisconnect; function onDisconnect(id, cb) { return (connId) => { if (id === connId) { cb(id); } }; } function init() { webSocketStrategy.registerMessageHandlers(); elipcStrategy.registerMessageHandlers(); } exports.init = init; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); let BrowserWindow = require('electron').BrowserWindow; let electronApp = require('electron').app; let _ = require('underscore'); let Application = require('../../api/application.js').Application; let apiProtocolBase = require('./api_protocol_base.js'); let coreState = require('../../core_state.js'); const of_events_1 = require("../../of_events"); const remote_subscriptions_1 = require("../../remote_subscriptions"); const route_1 = require("../../../common/route"); const SetWindowPosition = { SWP_HIDEWINDOW: 0x0080, SWP_SHOWWINDOW: 0x0040 }; const SysCommands = { SC_MAXIMIZE: 0xF030, SC_MINIMIZE: 0xF020, SC_RESTORE: 0xF120 }; const WindowsMessages = { WM_DESTROY: 0x0002, WM_SETFOCUS: 0x0007, WM_KILLFOCUS: 0x0008, WM_WINDOWPOSCHANGING: 0x0046, WM_WINDOWPOSCHANGED: 0x0047, WM_SYSCOMMAND: 0x0112, WM_NCLBUTTONDBLCLK: 0x00A3, WM_SIZING: 0x0214, WM_MOVING: 0x0216, WM_ENTERSIZEMOVE: 0x0231, WM_EXITSIZEMOVE: 0x0232 }; let successAck = { success: true }; module.exports.applicationApiMap = { 'close-application': closeApplication, 'create-application': createApplication, 'create-child-window': createChildWindow, 'deregister-external-window': deregisterExternalWindow, 'external-window-action': externalWindowAction, 'get-application-groups': getApplicationGroups, 'get-application-manifest': getApplicationManifest, 'get-application-zoom-level': getApplicationZoomLevel, 'get-child-windows': getChildWindows, 'get-info': getInfo, 'get-parent-application': getParentApplication, 'get-shortcuts': getShortcuts, 'get-tray-icon-info': getTrayIconInfo, 'is-application-running': isApplicationRunning, 'notify-on-app-connected': notifyOnAppConnected, 'notify-on-content-loaded': notifyOnContentLoaded, 'ping-child-window': pingChildWindow, 'register-external-window': registerExternalWindow, 'register-user': registerUser, 'relaunch-on-close': relaunchOnClose, 'remove-tray-icon': removeTrayIcon, 'restart-application': restartApplication, 'run-application': runApplication, 'send-application-log': sendApplicationLog, 'set-app-log-username': setAppLogUsername, 'set-shortcuts': { apiFunc: setShortcuts, apiPath: '.setShortcuts' }, 'set-tray-icon': setTrayIcon, 'set-application-zoom-level': setApplicationZoomLevel, 'terminate-application': terminateApplication, 'wait-for-hung-application': waitForHungApplication }; module.exports.init = function () { apiProtocolBase.registerActionMap(module.exports.applicationApiMap, 'Application'); }; function sendApplicationLog(identity, message, ack) { const payload = message.payload; const appIdentity = apiProtocolBase.getTargetApplicationIdentity(payload); return Application.sendApplicationLog(appIdentity).then((logId) => { const dataAck = _.clone(successAck); dataAck.data = logId; ack(dataAck); }); } function setTrayIcon(identity, rawMessage, ack, nack) { let message = JSON.parse(JSON.stringify(rawMessage)); let payload = message.payload; let appIdentity = apiProtocolBase.getTargetApplicationIdentity(payload); Application.setTrayIcon(appIdentity, payload.enabledIcon, () => { ack(successAck); }, nack); } function setApplicationZoomLevel(identity, rawMessage, ack) { const message = JSON.parse(JSON.stringify(rawMessage)); const payload = message.payload; const appIdentity = apiProtocolBase.getTargetApplicationIdentity(payload); Application.setZoomLevel(appIdentity, payload.level); ack(successAck); } function getTrayIconInfo(identity, message, ack, nack) { const dataAck = _.clone(successAck); const appIdentity = apiProtocolBase.getTargetApplicationIdentity(message.payload); Application.getTrayIconInfo(appIdentity, response => { dataAck.data = response; ack(dataAck); }, nack); } function removeTrayIcon(identity, message, ack) { const appIdentity = apiProtocolBase.getTargetApplicationIdentity(message.payload); Application.removeTrayIcon(appIdentity); ack(successAck); } function waitForHungApplication(identity, message, ack) { const appIdentity = apiProtocolBase.getTargetApplicationIdentity(message.payload); Application.wait(appIdentity); ack(successAck); } function terminateApplication(identity, message, ack) { const appIdentity = apiProtocolBase.getTargetApplicationIdentity(message.payload); Application.terminate(appIdentity, () => { ack(successAck); }); } function restartApplication(identity, message, ack) { const appIdentity = apiProtocolBase.getTargetApplicationIdentity(message.payload); Application.restart(appIdentity); ack(successAck); } function createChildWindow(identity, message, ack) { let payload = message.payload; let targetIdentity = { uuid: payload.targetUuid, name: payload.targetUuid }; let createChildPayload = { action: 'create-child-window', payload }; apiProtocolBase.sendToIdentity(targetIdentity, createChildPayload); ack(successAck); } function pingChildWindow(identity, message, ack) { ack(successAck); } function isApplicationRunning(identity, message, ack) { const dataAck = _.clone(successAck); const appIdentity = apiProtocolBase.getTargetApplicationIdentity(message.payload); dataAck.data = Application.isRunning(appIdentity); ack(dataAck); } function getApplicationManifest(identity, message, ack, nack) { const payload = message.payload; const dataAck = _.clone(successAck); let appIdentity; if (!payload.hasOwnProperty('manifestUrl')) { appIdentity = apiProtocolBase.getTargetApplicationIdentity(payload); } Application.getManifest(appIdentity, payload.manifestUrl, manifest => { dataAck.data = manifest; ack(dataAck); }, nack); } function getApplicationGroups(identity, message, ack) { const payload = message.payload; const dataAck = _.clone(successAck); const appIdentity = apiProtocolBase.getTargetApplicationIdentity(payload); let groups = _.filter(Application.getGroups(), windowGroup => { return _.some(windowGroup, window => { return window.uuid === appIdentity.uuid; }); }); dataAck.data = _.map(groups, groupOfWindows => { return _.map(groupOfWindows, window => { if (payload.crossApp === true) { return { uuid: window.uuid, name: window.name, windowName: window.name }; } else { return window.name; } }); }); ack(dataAck); } function getApplicationZoomLevel(identity, message, ack) { const dataAck = _.clone(successAck); const appIdentity = apiProtocolBase.getTargetApplicationIdentity(message.payload); Application.getZoomLevel(appIdentity, level => { dataAck.data = level; ack(dataAck); }); } function getChildWindows(identity, message, ack) { const dataAck = _.clone(successAck); const appIdentity = apiProtocolBase.getTargetApplicationIdentity(message.payload); dataAck.data = _.chain(Application.getChildWindows(appIdentity)) .filter(function (c) { return c.name !== c.uuid; }) .map(function (c) { return c.name; }) .value(); ack(dataAck); } function getInfo(identity, message, ack, nack) { const dataAck = _.clone(successAck); const appIdentity = apiProtocolBase.getTargetApplicationIdentity(message.payload); Application.getInfo(appIdentity, response => { dataAck.data = response; ack(dataAck); }, nack); } function getParentApplication(identity, message, ack, nack) { const dataAck = _.clone(successAck); const appIdentity = apiProtocolBase.getTargetApplicationIdentity(message.payload); const parentUuid = Application.getParentApplication(appIdentity); if (parentUuid) { dataAck.data = parentUuid; ack(dataAck); } else { nack(new Error('No parent application found')); } } function getShortcuts(identity, message, ack, nack) { const dataAck = _.clone(successAck); const appIdentity = apiProtocolBase.getTargetApplicationIdentity(message.payload); Application.getShortcuts(appIdentity, response => { dataAck.data = response; ack(dataAck); }, nack); } function setShortcuts(identity, message, ack, nack) { const payload = message.payload; const dataAck = _.clone(successAck); const appIdentity = apiProtocolBase.getTargetApplicationIdentity(payload); Application.setShortcuts(appIdentity, payload.data, response => { dataAck.data = response; ack(dataAck); }, nack); } function setAppLogUsername(identity, message, ack) { const payload = message.payload; const appIdentity = apiProtocolBase.getTargetApplicationIdentity(payload); return Application.setAppLogUsername(appIdentity, payload.data).then(() => ack(successAck)); } function closeApplication(identity, message, ack) { const payload = message.payload; const appIdentity = apiProtocolBase.getTargetApplicationIdentity(payload); const force = !!payload.force; Application.close(appIdentity, force, () => { ack(successAck); }); } function createApplication(identity, message, ack) { let payload = message.payload; Application.create(payload, undefined, identity); ack(successAck); } function notifyOnAppConnected(identity, message, ack) { var payload = message.payload; Application.notifyOnAppConnected({ uuid: payload.targetUuid, name: payload.name }, identity); ack(successAck); } function notifyOnContentLoaded(identity, message, ack) { var payload = message.payload; Application.notifyOnContentLoaded({ uuid: payload.targetUuid, name: payload.name }, identity); ack(successAck); } function runApplication(identity, message, ack, nack) { const { payload } = message; const { manifestUrl } = payload; const appIdentity = apiProtocolBase.getTargetApplicationIdentity(payload); const { uuid } = appIdentity; let remoteSubscriptionUnSubscribe; const remoteSubscription = { uuid, name: uuid, listenType: 'once', className: 'window', eventName: 'fire-constructor-callback' }; if (coreState.getAppRunningState(uuid)) { Application.emitRunRequested(appIdentity); nack(`Application with specified UUID is already running: ${uuid}`); return; } of_events_1.default.once(route_1.default.window('fire-constructor-callback', uuid, uuid), loadInfo => { if (loadInfo.success) { const successReturn = _.clone(successAck); successReturn.data = loadInfo.data; ack(successReturn); } else { const theErr = new Error(loadInfo.data.message); theErr.networkErrorCode = loadInfo.data.networkErrorCode; nack(theErr); } if (typeof remoteSubscriptionUnSubscribe === 'function') { remoteSubscriptionUnSubscribe(); } }); if (manifestUrl) { remote_subscriptions_1.addRemoteSubscription(remoteSubscription).then((unSubscribe) => { remoteSubscriptionUnSubscribe = unSubscribe; Application.runWithRVM(identity, manifestUrl).catch(nack); }); } else { Application.run(appIdentity); } } function registerExternalWindow(identity, message, ack) { let payload = message.payload; let childWindowOptions = { name: payload.name, uuid: payload.uuid, hwnd: payload.hwnd }; if (payload.options) { childWindowOptions = Object.assign(childWindowOptions, payload.options); } let parent = coreState.getWindowByUuidName(payload.uuid, payload.uuid); let parentBw = parent && parent.browserWindow; let childBw = new BrowserWindow(childWindowOptions); electronApp.emit('child-window-created', parentBw.id, childBw.id, childWindowOptions); ack(successAck); } function deregisterExternalWindow(identity, message, ack) { const windowIdentity = apiProtocolBase.getTargetWindowIdentity(message.payload); of_events_1.default.emit(route_1.default.externalWindow('close', windowIdentity.uuid, windowIdentity.name)); ack(successAck); } function externalWindowAction(identity, message, ack) { const { payload, payload: { type, uuid, name } } = message; switch (type) { case WindowsMessages.WM_DESTROY: of_events_1.default.emit(route_1.default.externalWindow('close', uuid, name)); break; case WindowsMessages.WM_SETFOCUS: of_events_1.default.emit(route_1.default.externalWindow('focus', uuid, name)); break; case WindowsMessages.WM_KILLFOCUS: of_events_1.default.emit(route_1.default.externalWindow('blur', uuid, name)); break; case WindowsMessages.WM_WINDOWPOSCHANGED: let flags = payload.flags; of_events_1.default.emit(route_1.default.externalWindow('bounds-changed', uuid, name)); if (flags & SetWindowPosition.SWP_SHOWWINDOW) { of_events_1.default.emit(route_1.default.externalWindow('visibility-changed', uuid, name), true); } else if (flags & SetWindowPosition.SWP_HIDEWINDOW) { of_events_1.default.emit(route_1.default.externalWindow('visibility-changed', uuid, name), false); } break; case WindowsMessages.WM_SYSCOMMAND: let commandType = payload.wParam; let stateChange = (commandType === SysCommands.SC_MAXIMIZE || commandType === SysCommands.SC_MINIMIZE || commandType === SysCommands.SC_RESTORE); if (!stateChange) { break; } case WindowsMessages.WM_NCLBUTTONDBLCLK: of_events_1.default.emit(route_1.default.externalWindow('state-change', uuid, name)); break; case WindowsMessages.WM_SIZING: of_events_1.default.emit(route_1.default.externalWindow('sizing', uuid, name)); break; case WindowsMessages.WM_MOVING: electronApp.vlog(1, `JavaLog moving ${uuid} ${name} ${JSON.stringify(payload)}`); let bounds = { x: payload.left, y: payload.top, width: payload.right - payload.left, height: payload.bottom - payload.top }; if (payload.original) { bounds.original = { x: payload.original.left, y: payload.original.top, width: payload.original.right - payload.original.left, height: payload.original.bottom - payload.original.top }; } electronApp.vlog(1, `JavaLog moving ${uuid} ${name} ${JSON.stringify(bounds)}`); of_events_1.default.emit(route_1.default.externalWindow('moving', uuid, name), bounds); break; case WindowsMessages.WM_ENTERSIZEMOVE: electronApp.vlog(1, `JavaLog begin-user-bounds-change ${uuid} ${name} `); of_events_1.default.emit(route_1.default.externalWindow('begin-user-bounds-change', uuid, name), { x: payload.mouseX, y: payload.mouseY }); break; case WindowsMessages.WM_EXITSIZEMOVE: electronApp.vlog(1, `JavaLog end-user-bounds-change ${JSON.stringify(payload)} `); let endBounds; if (payload.top) { endBounds = { x: payload.left, y: payload.top, width: payload.right - payload.left, height: payload.bottom - payload.top }; endBounds.original = Object.assign({}, endBounds); } of_events_1.default.emit(route_1.default.externalWindow('end-user-bounds-change', uuid, name), endBounds); break; default: break; } ack(successAck); } function registerUser(identity, message, ack, nack) { const payload = message.payload; const appIdentity = apiProtocolBase.getTargetApplicationIdentity(payload); Application.registerUser(appIdentity, payload.userName, payload.appName, () => { ack(successAck); }, nack); } function relaunchOnClose(identity, message, ack, nack) { const appIdentity = apiProtocolBase.getTargetApplicationIdentity(message.payload); Application.scheduleRestart(appIdentity, () => { ack(successAck); }, nack); } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); let fs = require('fs'); let apiProtocolBase = require('./api_protocol_base.js'); const external_application_1 = require("../../api/external_application"); let coreState = require('../../core_state.js'); const of_events_1 = require("../../of_events"); let _ = require('underscore'); let log = require('../../log'); let socketServer = require('../../transports/socket_server').server; let ProcessTracker = require('../../process_tracker.js'); const rvmMessageBus = require('../../rvm/rvm_message_bus').rvmMessageBus; const route_1 = require("../../../common/route"); const successAck = { success: true }; const AUTH_TYPE = { file: 0, sponsored: 1 }; var pendingAuthentications = new Map(), electronApp = require('app'), authenticationApiMap = { 'request-external-authorization': onRequestExternalAuth, 'request-authorization': onRequestAuthorization, 'register-external-connection': { apiFunc: registerExternalConnection, apiPath: 'System.registerExternalConnection' } }; function registerExternalConnection(identity, message, ack) { let uuidToRegister = message.payload.uuid; let token = electronApp.generateGUID(); let dataAck = _.clone(successAck); dataAck.data = { uuid: uuidToRegister, token }; addPendingAuthentication(uuidToRegister, token, null, identity, null); ack(dataAck); } function onRequestExternalAuth(id, message) { console.log('processing request-external-authorization', message); let { uuid: uuidRequested, pid } = message.payload; let extProcess, file, token; if (pid) { extProcess = ProcessTracker.getProcessByPid(pid) || ProcessTracker.monitor({ uuid: null, name: null }, { pid, uuid: uuidRequested, monitor: false }); } const uuid = (extProcess || {}).uuid || uuidRequested || electronApp.generateGUID(); if (pendingAuthentications.has(uuid)) { return; } file = getAuthFile(); token = electronApp.generateGUID(); addPendingAuthentication(uuid, token, file, null, message.payload); socketServer.send(id, JSON.stringify({ action: 'external-authorization-response', payload: { file, token, uuid } })); } function onRequestAuthorization(id, data) { const uuid = data.payload.uuid; const authObj = pendingAuthentications.get(uuid); const externalConnObj = Object.assign({}, data.payload, { id }); if (authObj && authObj.authReqPayload) { externalConnObj.configUrl = authObj.authReqPayload.configUrl; } const externalApplicationOptions = external_application_1.ExternalApplication.createExternalApplicationOptions(Object.assign({}, authObj.authReqPayload, externalConnObj)); authenticateUuid(authObj, data.payload, (success, error) => { let authorizationResponse = { action: 'authorization-response', payload: { success: success } }; if (!success) { authorizationResponse.payload.reason = error || 'Invalid token or file'; } socketServer.send(id, JSON.stringify(authorizationResponse)); if (success) { external_application_1.ExternalApplication.addExternalConnection(externalApplicationOptions); socketServer.connectionAuthenticated(id, uuid); rvmMessageBus.registerLicenseInfo({ data: { licenseKey: externalApplicationOptions.licenseKey, client: externalApplicationOptions.client, uuid, parentApp: { uuid: null, configUrl: null } } }, externalApplicationOptions.configUrl); } else { socketServer.closeConnection(id); } cleanPendingRequest(authObj); }); } function getAuthFile() { return `${electronApp.getPath('userData')}-${electronApp.generateGUID()}`; } function addPendingAuthentication(uuid, token, file, sponsor, authReqPayload) { let authObj = { uuid, token, file, sponsor, authReqPayload }; authObj.type = file ? AUTH_TYPE.file : AUTH_TYPE.sponsored; pendingAuthentications.set(uuid, authObj); } function authenticateUuid(authObj, authRequest, cb) { if (external_application_1.ExternalApplication.getExternalConnectionByUuid(authRequest.uuid) || coreState.getAppByUuid(authRequest.uuid)) { cb(false, 'Application with specified UUID already exists: ' + authRequest.uuid); } else if (!authObj) { cb(false, 'Invalid UUID: ' + authRequest.uuid); } else if (authObj.type === AUTH_TYPE.file) { try { fs.readFile(authObj.file, (err, data) => { cb(data.toString().indexOf(authObj.token) >= 0); }); } catch (err) { console.log(err); } } else { cb(authObj.token === authRequest.token); } } function cleanPendingRequest(authObj) { if (authObj && authObj.type === AUTH_TYPE.file) { fs.unlink(authObj.file, err => { log.writeToLog('info', err); pendingAuthentications.delete(authObj.uuid); }); } } module.exports.init = function () { socketServer.on(route_1.default.connection('close'), id => { var keyToDelete, externalConnection; for (var [key, value] of pendingAuthentications.entries()) { if (value.id === id) { pendingAuthentications.delete(key); break; } } pendingAuthentications.delete(keyToDelete); externalConnection = external_application_1.ExternalApplication.getExternalConnectionById(id); if (externalConnection) { external_application_1.ExternalApplication.removeExternalConnection(externalConnection); of_events_1.default.emit(route_1.default('externalconn', 'closed'), externalConnection); } if (coreState.shouldCloseRuntime()) { electronApp.quit(); } }); apiProtocolBase.registerActionMap(authenticationApiMap); }; const isConnectionAuthenticated = (msg, next) => { const { data, nack, identity, strategyName } = msg; const { runtimeUuid, uuid } = identity; const action = data && data.action; const uuidToCheck = runtimeUuid || uuid; if (strategyName === 'WebSocketStrategy' && !authenticationApiMap.hasOwnProperty(action) && !external_application_1.ExternalApplication.getExternalConnectionByUuid(uuidToCheck)) { return nack(new Error('This connection must be authenticated first')); } next(); }; module.exports.registerMiddleware = function (requestHandler) { requestHandler.addPreProcessor(isConnectionAuthenticated); }; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const apiProtocolBase = require("./api_protocol_base"); const channel_1 = require("../../api/channel"); const successAck = { success: true }; class ChannelApiHandler { constructor() { this.actionMap = { 'connect-to-channel': this.connectToChannel, 'create-channel': this.createChannel, 'destroy-channel': this.destroyChannel, 'disconnect-from-channel': this.disconnectFromChannel, 'get-all-channels': this.getAllChannels, 'send-channel-message': this.sendChannelMessage, 'send-channel-result': this.sendChannelResult }; apiProtocolBase.registerActionMap(this.actionMap); } connectToChannel(identity, message, ack, nack) { const { payload, messageId, locals } = message; if (locals && locals.aggregate) { const { aggregate } = locals; if (Array.isArray(aggregate) && aggregate.length) { const channel = aggregate.filter(c => !!c); if (channel.length > 1) { nack(`Runtime Error: More than one channel for channelName ${payload.channelName}`); } else { const dataAck = Object.assign({}, successAck, { data: channel[0] }); ack(dataAck); return; } } } channel_1.Channel.connectToChannel(identity, payload, messageId, ack, nack); return undefined; } createChannel(identity, message, ack) { const { payload, locals } = message; const { channelName } = payload; let allChannels = channel_1.Channel.getAllChannels(); if (locals && locals.aggregate) { const { aggregate } = locals; allChannels = [...allChannels, ...aggregate]; } const providerIdentity = channel_1.Channel.createChannel(identity, channelName, allChannels); const dataAck = Object.assign({}, successAck, { data: providerIdentity }); ack(dataAck); } destroyChannel(identity, message, ack) { const { payload: { channelName } } = message; channel_1.Channel.destroyChannel(identity, channelName); ack(successAck); } disconnectFromChannel(identity, message, ack) { const { payload: { channelName } } = message; channel_1.Channel.disconnectFromChannel(identity, channelName); ack(successAck); } getAllChannels(identity, message, ack) { const { locals } = message; let allChannels = channel_1.Channel.getAllChannels(); if (locals && locals.aggregate) { const { aggregate } = locals; allChannels = [...allChannels, ...aggregate]; } return allChannels; } sendChannelMessage(identity, message, ack, nack) { const { payload, messageId } = message; channel_1.Channel.sendChannelMessage(identity, payload, messageId, ack, nack); return undefined; } sendChannelResult(identity, message, ack, nack) { const { payload } = message; channel_1.Channel.sendChannelResult(identity, payload, ack, nack); return undefined; } } exports.ChannelApiHandler = ChannelApiHandler; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const api_protocol_base_1 = require("./api_protocol_base"); const electron_1 = require("electron"); const clipboardApiMap = { 'clipboard-clear': clipboardClear, 'clipboard-read-formats': { apiFunc: clipboardAvailableFormats, apiPath: '.availableFormats' }, 'clipboard-read-html': { apiFunc: clipboardReadHtml, apiPath: '.readHtml' }, 'clipboard-read-rtf': { apiFunc: clipboardReadRtf, apiPath: '.readRtf' }, 'clipboard-read-text': { apiFunc: clipboardReadText, apiPath: '.readText' }, 'clipboard-write': { apiFunc: clipboardWrite, apiPath: '.write' }, 'clipboard-write-html': { apiFunc: clipboardWriteHtml, apiPath: '.writeHtml' }, 'clipboard-write-rtf': { apiFunc: clipboardWriteRtf, apiPath: '.writeRtf' }, 'clipboard-write-text': { apiFunc: clipboardWriteText, apiPath: '.writeText' }, 'set-clipboard': { apiFunc: clipboardWriteText, apiPath: 'System.setClipboard' } }; function init() { api_protocol_base_1.registerActionMap(clipboardApiMap, 'System.Clipboard'); } exports.init = init; function clipboardWrite(identity, message, ack) { const { data, type } = message.payload; ack({ success: true, data: electron_1.clipboard.write(data, type) }); } function clipboardWriteRtf(identity, message, ack) { const { data, type } = message.payload; ack({ success: true, data: electron_1.clipboard.writeRTF(data, type) }); } function clipboardWriteHtml(identity, message, ack) { const { data, type } = message.payload; ack({ success: true, data: electron_1.clipboard.writeHTML(data, type) }); } function clipboardWriteText(identity, message, ack) { const { data, type } = message.payload; ack({ success: true, data: electron_1.clipboard.writeText(data, type) }); } function clipboardAvailableFormats(identity, message, ack) { const { type } = message.payload; ack({ success: true, data: electron_1.clipboard.availableFormats(type) }); } function clipboardClear(identity, message, ack) { const { type } = message.payload; electron_1.clipboard.clear(type); ack({ success: true }); } function clipboardReadRtf(identity, message, ack) { const { type } = message.payload; ack({ success: true, data: electron_1.clipboard.readRTF(type) }); } function clipboardReadHtml(identity, message, ack) { const { type } = message.payload; ack({ success: true, data: electron_1.clipboard.readHTML(type) }); } function clipboardReadText(identity, message, ack) { const { type } = message.payload; ack({ success: true, data: electron_1.clipboard.readText(type) }); } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const api_protocol_base_1 = require("./api_protocol_base"); const coreState = require('../../core_state'); const validExternalAPIActions = { 'blur-window': true, 'bring-window-to-front': true, 'close-window': true, 'focus-window': true, 'hide-window': true, 'maximize-window': true, 'minimize-window': true, 'move-window-by': true, 'move-window': true, 'resize-window-by': true, 'resize-window': true, 'restore-window': true, 'show-window': true, 'show-at-window': true, 'set-foreground-window': true }; const remoteAckMap = new Map(); const EXTERNAL_APP_ACTION = 'process-external-app-action'; const EXTERNAL_ACK_ACTION = 'external-ack'; const LEGACY_WINDOW_FLAG = 'enabled-deprecated-external-windowing'; function getAckKey(id, uuid) { return `${id}-${uuid}`; } function handleExternalApiAction(msg, next) { const { data, ack, nack, identity } = msg; const payload = data && data.payload; const uuid = payload && payload.uuid; const name = payload && payload.name; const action = data && data.action; if (name && uuid && validExternalAPIActions[action]) { const externalConn = coreState.getExternalAppObjByUuid(uuid); const ackKey = getAckKey(data.messageId, identity.uuid); if (externalConn) { remoteAckMap.set(ackKey, { ack, nack }); api_protocol_base_1.sendToIdentity({ uuid: externalConn.uuid }, { action: EXTERNAL_APP_ACTION, payload: { action, messageId: data.messageId, payload, destinationToken: identity.uuid } }); } else { next(); } } else { next(); } } function handleExternalAckAction(msg, next) { const { data } = msg; const action = data && data.action; if (action === EXTERNAL_ACK_ACTION) { const uuid = data.destinationToken; const ackKey = getAckKey(data.correlationId, uuid); const remoteAck = remoteAckMap.get(ackKey); if (remoteAck) { if (data.success) { remoteAck.ack({ success: true }); } else { remoteAck.nack(new Error(data.reason)); } remoteAckMap.delete(ackKey); } else { next(); } } else { next(); } } function registerMiddleware(requestHandler) { requestHandler.addPreProcessor(handleExternalApiAction); requestHandler.addPreProcessor(handleExternalAckAction); } exports.registerMiddleware = registerMiddleware; function legacyWindowingEnabled() { const enabled = coreState.argo[LEGACY_WINDOW_FLAG]; return enabled !== void 0; } exports.legacyWindowingEnabled = legacyWindowingEnabled; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const subscriptions_1 = require("../../api/notifications/subscriptions"); const remote_subscriptions_1 = require("../../remote_subscriptions"); const application_1 = require("../../api/application"); const channel_1 = require("../../api/channel"); const external_application_1 = require("../../api/external_application"); const frame_1 = require("../../api/frame"); const core_state_1 = require("../../core_state"); const global_hotkey_1 = require("../../api/global_hotkey"); const main_1 = require("../../../common/main"); const system_1 = require("../../api/system"); const window_1 = require("../../api/window"); const _ = require("underscore"); const apiProtocolBase = require("./api_protocol_base"); const of_events_1 = require("../../of_events"); const successAck = { success: true }; const eventMap = { 'subscribe-to-desktop-event': subToDesktopEvent, 'unsubscribe-to-desktop-event': unSubToDesktopEvent }; function init() { apiProtocolBase.registerActionMap(eventMap); } exports.init = init; const subWindow = async (identity, eventName, payload, listener) => { const { uuid, name } = payload; const windowIdentity = apiProtocolBase.getTargetWindowIdentity(payload); const targetUuid = windowIdentity.uuid; const islocalWindow = !!core_state_1.getWindowByUuidName(targetUuid, targetUuid); const localUnsub = window_1.Window.addEventListener(identity, windowIdentity, eventName, listener); const isExternalClient = external_application_1.ExternalApplication.isRuntimeClient(identity.uuid); let remoteUnSub = main_1.noop; if (!islocalWindow && !isExternalClient) { const subscription = { className: 'window', eventName, listenType: 'on', name, uuid }; remoteUnSub = await remote_subscriptions_1.addRemoteSubscription(subscription); } return () => { localUnsub(); remoteUnSub(); }; }; const subFrame = async (identity, eventName, payload, listener) => { const { uuid, name } = payload; const frameIdentity = apiProtocolBase.getTargetWindowIdentity(payload); const targetUuid = frameIdentity.uuid; const islocalWindow = !!core_state_1.getWindowByUuidName(targetUuid, targetUuid); const localUnsub = frame_1.Frame.addEventListener(frameIdentity, eventName, listener); const isExternalClient = external_application_1.ExternalApplication.isRuntimeClient(identity.uuid); let remoteUnSub = main_1.noop; if (!islocalWindow && !isExternalClient) { const subscription = { className: 'frame', eventName, listenType: 'on', name, uuid }; remoteUnSub = await remote_subscriptions_1.addRemoteSubscription(subscription); } return () => { localUnsub(); remoteUnSub(); }; }; const subApplication = async (identity, eventName, payload, listener) => { const { uuid } = payload; const appIdentity = apiProtocolBase.getTargetApplicationIdentity(payload); const targetUuid = appIdentity.uuid; const islocalApp = !!core_state_1.getWindowByUuidName(targetUuid, targetUuid); const localUnsub = application_1.Application.addEventListener(appIdentity, eventName, listener); const isExternalClient = external_application_1.ExternalApplication.isRuntimeClient(identity.uuid); let remoteUnSub = main_1.noop; if (!islocalApp && !isExternalClient) { const subscription = { className: 'application', eventName, listenType: 'on', uuid }; remoteUnSub = await remote_subscriptions_1.addRemoteSubscription(subscription); } return () => { localUnsub(); remoteUnSub(); }; }; const subChannel = async (identity, eventName, payload, listener) => { const targetIdentity = apiProtocolBase.getTargetWindowIdentity(payload); const { uuid } = targetIdentity; const islocalUuid = core_state_1.isLocalUuid(uuid); const localUnsub = channel_1.Channel.addEventListener(targetIdentity, eventName, listener); const isExternalClient = external_application_1.ExternalApplication.isRuntimeClient(identity.uuid); let remoteUnSub = main_1.noop; if (!islocalUuid && !isExternalClient && (eventName === 'connected' || eventName === 'disconnected')) { const subscription = { className: 'channel', eventName, listenType: 'on' }; remoteUnSub = await remote_subscriptions_1.subscribeToAllRuntimes(subscription); } return () => { localUnsub(); remoteUnSub(); }; }; const subSystem = async (identity, eventName, payload, listener) => { const localUnsub = system_1.System.addEventListener(eventName, listener); const isExternalClient = external_application_1.ExternalApplication.isRuntimeClient(identity.uuid); let remoteUnSub = main_1.noop; if (!isExternalClient) { const subscription = { className: 'system', eventName, listenType: 'on' }; remoteUnSub = await remote_subscriptions_1.subscribeToAllRuntimes(subscription); } return () => { localUnsub(); remoteUnSub(); }; }; const subNotifications = async (identity, eventName, payload, listener) => { return subscriptions_1.addEventListener(identity, eventName, payload, listener); }; const subExternalApp = async (identity, eventName, payload, listener) => { const { uuid } = payload; const externalAppIdentity = { uuid }; return external_application_1.ExternalApplication.addEventListener(externalAppIdentity, eventName, listener); }; const subGlobalHotkey = async (identity, eventName, payload, listener) => { return global_hotkey_1.GlobalHotkey.addEventListener(identity, eventName, listener); }; const subscriptionMap = { 'application': subApplication, 'channel': subChannel, 'external-application': subExternalApp, 'frame': subFrame, 'global-hotkey': subGlobalHotkey, 'notifications': subNotifications, 'system': subSystem, 'window': subWindow }; async function subToDesktopEvent(identity, message, ack) { const { payload } = message; const { name, topic, type, uuid } = payload; const subscribe = subscriptionMap[topic]; const listener = (emittedPayload) => { const event = { action: 'process-desktop-event', payload: { topic, type, uuid } }; if (name) { event.payload.name = name; } if (!uuid && emittedPayload.uuid) { event.payload.uuid = emittedPayload.uuid; } if (typeof emittedPayload === 'object') { _.extend(event.payload, _.omit(emittedPayload, _.keys(event.payload))); } apiProtocolBase.sendToIdentity(identity, event); }; if (apiProtocolBase.subscriptionExists(identity, topic, uuid, type, name)) { apiProtocolBase.uppSubscriptionRefCount(identity, topic, uuid, type, name); } else if (typeof subscribe === 'function') { const unsubscribe = await subscribe(identity, type, payload, listener); apiProtocolBase.registerSubscription(unsubscribe, identity, topic, uuid, type, name); } ack(successAck); of_events_1.default.checkMissedEvents(payload, listener); } function unSubToDesktopEvent(identity, message, ack) { const { payload } = message; const { name, topic, type, uuid } = payload; apiProtocolBase.removeSubscription(identity, topic, uuid, type, name); ack(successAck); } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const apiProtocolBase = require("./api_protocol_base"); const external_application_1 = require("../../api/external_application"); class ExternalApplicationApiHandler { constructor() { this.actionMap = { 'get-external-application-info': this.getInfo }; apiProtocolBase.registerActionMap(this.actionMap); } getInfo(source, message) { return external_application_1.ExternalApplication.getInfo(message.payload); } } exports.ExternalApplicationApiHandler = ExternalApplicationApiHandler; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const apiProtocolBase = require("./api_protocol_base"); const frame_1 = require("../../api/frame"); class FrameApiHandler { constructor() { this.actionMap = { 'get-frame-info': this.getInfo, 'get-parent-window': this.getParentWindow }; apiProtocolBase.registerActionMap(this.actionMap); } getInfo(identity, message) { const frameIdentity = apiProtocolBase.getTargetWindowIdentity(message.payload); return frame_1.Frame.getInfo(frameIdentity); } getParentWindow(identity) { return frame_1.Frame.getParentWindow(identity); } } exports.FrameApiHandler = FrameApiHandler; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const apiProtocolBase = require("./api_protocol_base"); const global_hotkey_1 = require("../../api/global_hotkey"); const successAck = { success: true }; const PROCESS_DESKTOP_EVENT = 'process-desktop-event'; const EVENT_TOPIC = 'global-hotkey'; class GlobalHotkeyApiHandler { constructor() { this.actionMap = { 'global-hotkey-is-registered': this.isRegistered, 'global-hotkey-register': this.register, 'global-hotkey-unregister': this.unregister, 'global-hotkey-unregister-all': this.unregisterAll }; apiProtocolBase.registerActionMap(this.actionMap); } register(source, message) { const { hotkey } = message.payload; global_hotkey_1.GlobalHotkey.register(source, hotkey, () => { const eventObj = { action: PROCESS_DESKTOP_EVENT, payload: { topic: EVENT_TOPIC, type: hotkey } }; apiProtocolBase.sendToIdentity(source, eventObj); }); return successAck; } unregister(source, message) { const { hotkey } = message.payload; global_hotkey_1.GlobalHotkey.unregister(source, hotkey); return successAck; } unregisterAll(source, message) { global_hotkey_1.GlobalHotkey.unregisterAll(source); return successAck; } isRegistered(source, message) { const { hotkey } = message.payload; return global_hotkey_1.GlobalHotkey.isRegistered(hotkey); } } exports.GlobalHotkeyApiHandler = GlobalHotkeyApiHandler; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const core_state_1 = require("../../core_state"); const disabled_frame_group_tracker_1 = require("../../disabled_frame_group_tracker"); const api_protocol_base_1 = require("./api_protocol_base"); const unsupported = (payload) => { throw new Error('This action is not supported while grouped'); }; const hijackThese = { 'animate-window': unsupported, 'disable-window-frame': unsupported, 'enable-window-frame': unsupported, 'move-window': makeGetChangeType(['left', 'top'], ['x', 'y'], 'absolute'), 'move-window-by': makeGetChangeType(['deltaLeft', 'deltaTop'], ['x', 'y'], 'delta'), 'resize-window': makeGetChangeType(['height', 'width'], ['height', 'width'], 'absolute'), 'resize-window-by': makeGetChangeType(['deltaHeight', 'deltaWidth'], ['height', 'width'], 'delta'), 'set-window-bounds': makeGetChangeType(['left', 'top', 'height', 'width'], ['x', 'y', 'height', 'width'], 'absolute'), 'show-at-window': makeGetChangeType(['left', 'top'], ['x', 'y'], 'absolute') }; function makeGetChangeType(from, to, change) { return (payload) => from.reduce((accum, key, i) => { accum[to[i]] = payload[key]; return accum; }, { change }); } function hijackMovesForGroupedWindows(actions) { const specMap = {}; Object.entries(actions).forEach(([action, endpoint]) => { if (!hijackThese[action]) { specMap[action] = endpoint; } else { if (typeof endpoint === 'function') { specMap[action] = (identity, message, ack, nack) => { const { payload } = message; const { uuid, name } = api_protocol_base_1.getTargetWindowIdentity(payload); const wrapped = core_state_1.getWindowByUuidName(uuid, name); if (wrapped && wrapped.groupUuid) { const changeType = hijackThese[action](payload); const moved = changeType.change === 'delta' ? disabled_frame_group_tracker_1.updateGroupedWindowBounds(wrapped, changeType) : disabled_frame_group_tracker_1.setNewGroupedWindowBounds(wrapped, changeType); if (action === 'show-at-window') { const showWindow = specMap['show-window']; showWindow(identity, message, ack, nack); } else { ack({ success: true }); } } else { endpoint(identity, message, ack, nack); } }; } } }); return specMap; } exports.hijackMovesForGroupedWindows = hijackMovesForGroupedWindows; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const apiProtocolBase = require("./api_protocol_base"); const interappbus_1 = require("../../api/interappbus"); const of_events_1 = require("../../of_events"); const route_1 = require("../../../common/route"); var SubscriptionTypes; (function (SubscriptionTypes) { SubscriptionTypes[SubscriptionTypes["MESSAGE"] = 1] = "MESSAGE"; SubscriptionTypes[SubscriptionTypes["SUB_ADDED"] = 2] = "SUB_ADDED"; SubscriptionTypes[SubscriptionTypes["SUB_REMOVED"] = 3] = "SUB_REMOVED"; })(SubscriptionTypes || (SubscriptionTypes = {})); const successAck = { success: true }; const interAppBusExternalApiMap = { 'publish-message': publishMessage, 'send-message': sendMessage, 'subscribe': subscribe, 'unsubscribe': unsubscribe, 'subscriber-added': subscriberAdded, 'subscriber-removed': subscriberRemoved }; function init() { apiProtocolBase.registerActionMap(interAppBusExternalApiMap); } exports.init = init; function unsubscribe(identity, message, ack) { const { payload } = message; const { topic } = payload; const sourceUuid = '*'; const sourceWindowName = '*'; const subscriptionArgs = [ topic, identity.uuid, sourceUuid, sourceWindowName, SubscriptionTypes.MESSAGE ]; apiProtocolBase.removeSubscription(identity, ...subscriptionArgs); if (apiProtocolBase.subscriptionExists(identity, ...subscriptionArgs)) { interappbus_1.InterApplicationBus.emitSubscriberRemoved(identity, payload); } ack(successAck); } function subscribe(identity, message, ack) { const { uuid, name } = identity; const { payload } = message; const { topic, messageKey: subscribedMessageKey } = payload; const sourceUuid = '*'; const sourceWindowName = '*'; const subscriptionCallback = (payload) => { const { messageKey: sentMessageKey } = payload; const command = { action: 'process-message', payload }; if (!subscribedMessageKey && (sentMessageKey === 'messageString')) { command.payload.message = JSON.parse(payload[sentMessageKey]); } apiProtocolBase.sendToIdentity(identity, command); }; const subscriptionArgs = [ topic, uuid, sourceUuid, sourceWindowName, SubscriptionTypes.MESSAGE ]; if (apiProtocolBase.subscriptionExists(identity, ...subscriptionArgs)) { apiProtocolBase.uppSubscriptionRefCount(identity, ...subscriptionArgs); interappbus_1.InterApplicationBus.emitSubscriberAdded(identity, payload); of_events_1.default.once(route_1.default.window('unload', uuid, name, false), () => { apiProtocolBase.removeSubscription(identity, ...subscriptionArgs); }); } else { const wildcardPayload = Object.assign({}, payload, { sourceUuid, sourceWindowName }); const subscriptionObj = interappbus_1.InterApplicationBus.subscribe(identity, wildcardPayload, subscriptionCallback); interappbus_1.InterApplicationBus.emitSubscriberAdded(identity, payload); const unsub = () => { interappbus_1.InterApplicationBus.emitSubscriberRemoved(identity, payload); subscriptionObj.unsubscribe(); }; apiProtocolBase.registerSubscription(unsub, identity, ...subscriptionArgs); of_events_1.default.once(route_1.default.window('unload', uuid, name, false), () => { apiProtocolBase.removeSubscription(identity, ...subscriptionArgs); }); } ack(successAck); } function sendMessage(identity, message, ack) { const { payload } = message; interappbus_1.InterApplicationBus.send(identity, payload); ack(successAck); } function publishMessage(identity, message, ack) { const { payload } = message; interappbus_1.InterApplicationBus.publish(identity, payload); ack(successAck); } function subscriberAdded(identity, message, ack) { const { payload } = message; interappbus_1.InterApplicationBus.raiseSubscriberEvent(of_events_1.default.subscriber.ADDED, payload); ack(successAck); } function subscriberRemoved(identity, message, ack) { const { payload } = message; interappbus_1.InterApplicationBus.raiseSubscriberEvent(of_events_1.default.subscriber.REMOVED, payload); ack(successAck); } function initSubscriptionListeners(connectionIdentity) { const { SUB_ADDED, SUB_REMOVED } = SubscriptionTypes; const { uuid, name } = connectionIdentity; const iabIdentity = { uuid, name: uuid }; const subAddedSubObj = interappbus_1.InterApplicationBus.subscriberAdded(iabIdentity, (subscriber) => { const { directMsg } = subscriber; const directedToId = directMsg === name; if (directMsg) { if (directedToId) { sendSubscriberEvent(connectionIdentity, subscriber, of_events_1.default.subscriber.ADDED); } } else { sendSubscriberEvent(connectionIdentity, subscriber, of_events_1.default.subscriber.ADDED); } }); const subRemovedSubObj = interappbus_1.InterApplicationBus.subscriberRemoved(iabIdentity, (subscriber) => { const { directMsg = null } = (subscriber || {}); const directedToId = directMsg === name; if (directMsg) { if (directedToId) { sendSubscriberEvent(connectionIdentity, subscriber, of_events_1.default.subscriber.REMOVED); } } else { sendSubscriberEvent(connectionIdentity, subscriber, of_events_1.default.subscriber.REMOVED); } }); apiProtocolBase.registerSubscription(subAddedSubObj.unsubscribe, iabIdentity, uuid, name, SUB_ADDED); apiProtocolBase.registerSubscription(subRemovedSubObj.unsubscribe, iabIdentity, uuid, name, SUB_REMOVED); of_events_1.default.once(route_1.default.window('unload', uuid, name, false), () => { apiProtocolBase.removeSubscription(iabIdentity, uuid, name, SUB_ADDED); apiProtocolBase.removeSubscription(iabIdentity, uuid, name, SUB_REMOVED); }); } of_events_1.default.on(route_1.default.window('init-subscription-listeners'), (identity) => { initSubscriptionListeners(identity); }); apiProtocolBase.onClientAuthenticated(initSubscriptionListeners); function sendSubscriberEvent(identity, subscriber, action) { const subscriberAdded = { action: action, payload: { senderName: subscriber.senderName, senderUuid: subscriber.senderUuid, targetName: subscriber.name, topic: subscriber.topic, uuid: subscriber.uuid } }; apiProtocolBase.sendToIdentity(identity, subscriberAdded); } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const log = require("../../log"); const connection_manager_1 = require("../../connection_manager"); const of_events_1 = require("../../of_events"); const core_state_1 = require("../../core_state"); const SUBSCRIBE_ACTION = 'subscribe'; const PUBLISH_ACTION = 'publish-message'; const SEND_MESSAGE_ACTION = 'send-message'; const apiMessagesToIgnore = { 'publish-message': true, 'send-message': true, 'subscribe': true, 'unsubscribe': true, 'subscriber-added': true, 'subscriber-removed': true, 'subscribe-to-desktop-event': true, 'unsubscribe-to-desktop-event': true }; const apiMessagesToAggregate = { 'create-channel': 35, 'connect-to-channel': 35, 'get-all-applications': 1, 'get-all-channels': 35, 'get-all-external-applications': 1, 'get-all-windows': 1, 'get-focused-window': 1, 'process-snapshot': 1 }; const subscriberTriggeredEvents = { 'subscribe': true, 'unsubscribe': true }; const filterRuntimes = (action, runtimes) => { const minimumVersion = apiMessagesToAggregate[action]; if (minimumVersion) { return runtimes.filter(runtime => { const ofVersion = runtime.portInfo.version.split('.')[2]; return +ofVersion >= +minimumVersion; }); } else { return runtimes; } }; function subscriberEventMiddleware(msg, next) { const { data, identity: { uuid: uuid }, identity } = msg; if (subscriberTriggeredEvents[data.action] && !identity.runtimeUuid) { const { payload: { sourceUuid: sourceUuid, topic: topic, destinationWindowName, sourceWindowName } } = data; const forwardedAction = data.action === SUBSCRIBE_ACTION ? of_events_1.default.subscriber.ADDED : of_events_1.default.subscriber.REMOVED; const subAddedPayload = { senderName: sourceWindowName || sourceUuid, senderUuid: sourceUuid, name: destinationWindowName, uuid, topic }; connection_manager_1.default.resolveIdentity({ uuid: sourceUuid }) .then((id) => { return id.runtime.fin.System.executeOnRemote(identity, { action: forwardedAction, payload: subAddedPayload }).catch((e) => { log.writeToLog('info', e); }); }); } next(); } function publishMiddleware(msg, next) { const { data, identity } = msg; if (data.action === PUBLISH_ACTION && !identity.runtimeUuid) { connection_manager_1.default.connections.forEach((peer) => { peer.fin.System.executeOnRemote(identity, data); }); } next(); } function sendMessageMiddleware(msg, next) { const { data, identity, ack, nack } = msg; if (data.action === SEND_MESSAGE_ACTION && !identity.runtimeUuid) { const { payload: { destinationUuid } } = data; if (core_state_1.isLocalUuid(destinationUuid)) { next(); } else { connection_manager_1.default.resolveIdentity({ uuid: destinationUuid }) .then((id) => { id.runtime.fin.System.executeOnRemote(identity, data) .then(ack) .catch(nack); }).catch((e) => { next(); }); } } else { next(); } } function ferryActionMiddleware(msg, next) { const { identity, data, ack, nack } = msg; const payload = data && data.payload; const uuid = payload && payload.uuid; const action = data && data.action; const isValidUuid = uuid !== void (0); const isValidIdentity = typeof (identity) === 'object'; const isForwardAction = !apiMessagesToIgnore[action]; const isRemoteEntity = !core_state_1.isLocalUuid(uuid); const isLocalAction = !identity.runtimeUuid; if (isValidUuid && isForwardAction && isValidIdentity && isRemoteEntity && isLocalAction) { try { connection_manager_1.default.resolveIdentity({ uuid }) .then((id) => { id.runtime.fin.System.executeOnRemote(identity, data) .then(res => { switch (action) { case 'get-info': if (res && res.data && !res.data.runtime) { Object.assign(res.data, { runtime: { version: id.runtime.portInfo.version } }); } return res; default: return res; } }) .then(ack) .catch(nack); }).catch((e) => { next(); }); } catch (e) { log.writeToLog('info', e); next(); } } else { next(); } } function aggregateFromExternalRuntime(msg, next) { const { identity, data, nack } = msg; const action = data && data.action; const isAggregateAction = apiMessagesToAggregate[action]; const isLocalAction = !identity.runtimeUuid; const filteredRuntimes = filterRuntimes(action, connection_manager_1.default.connections); try { if (isAggregateAction && isLocalAction && filteredRuntimes.length) { const aggregateData = JSON.parse(JSON.stringify(data)); if (action === 'create-channel') { aggregateData.action = 'get-all-channels'; } Promise.all(filteredRuntimes.map(runtime => runtime.fin.System.executeOnRemote(identity, aggregateData))) .then(externalResults => { const externalRuntimeData = externalResults.reduce((result, runtime) => { if (runtime && runtime.data) { if (Array.isArray(runtime.data)) { return [...result, ...runtime.data]; } else { return [...result, runtime.data]; } } return result; }, []); const locals = { aggregate: externalRuntimeData }; next(locals); }) .catch(nack); } else { next(); } } catch (e) { log.writeToLog('info', e); next(); } } function registerMiddleware(requestHandler) { requestHandler.addPreProcessor(subscriberEventMiddleware); requestHandler.addPreProcessor(publishMiddleware); requestHandler.addPreProcessor(sendMessageMiddleware); requestHandler.addPreProcessor(ferryActionMiddleware); requestHandler.addPreProcessor(aggregateFromExternalRuntime); } exports.registerMiddleware = registerMiddleware; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const core_state_1 = require("../../core_state"); const application_js_1 = require("./application.js"); const window_js_1 = require("./window.js"); const apisToIgnore = new Set([ 'create-application', 'create-child-window', 'is-application-running', 'register-window-name', 'run-application', 'window-exists', 'window-is-notification-type' ]); function verifyEntityExistence(msg, next) { const { data, nack } = msg; const payload = data && data.payload; const uuid = payload && payload.uuid; const name = payload && payload.name; const action = data && data.action; if (!uuid || apisToIgnore.has(action)) { return next(); } if (application_js_1.applicationApiMap.hasOwnProperty(action)) { const appExists = !!core_state_1.appByUuid(uuid); if (!appExists) { if (action === 'run-application' && payload.manifestUrl) { return next(); } return nack('Could not locate the requested application'); } } else if (window_js_1.windowApiMap.hasOwnProperty(action)) { const wndExists = core_state_1.windowExists(uuid, name); if (!wndExists) { return nack('Could not locate the requested window'); } } next(); } function registerMiddleware(requestHandler) { requestHandler.addPreProcessor(verifyEntityExistence); } exports.registerMiddleware = registerMiddleware; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const subscriptions_1 = require("../../api/notifications/subscriptions"); const note_action_1 = require("../../api/notifications/note_action"); const apiProtocolBase = require('./api_protocol_base.js'); const { writeToLog } = require('../../log'); function NotificationApiHandler() { const noteApiMap = { 'notifications': (id, request, ack) => { subscriptions_1.routeRequest(id, unpackGeneralMsg(request), ack); }, 'send-action-to-notifications-center': normalizeAndDispatch }; apiProtocolBase.registerActionMap(noteApiMap); return apiProtocolBase; } exports.NotificationApiHandler = NotificationApiHandler; function normalizeAndDispatch(id, msg, ack) { const { action } = msg; switch (action) { case 'send-action-to-notifications-center': routeNoteCenterMessages(id, msg, ack); break; default: break; } } function routeNoteCenterMessages(id, msg, ack) { const { payload: { action, payload: data } } = msg; switch (action) { case 'create-notification': writeToLog('info', msg); data.message = data.message && data.message.message; subscriptions_1.routeRequest(id, { action: note_action_1.default.create_external, data, id }, ack); break; case 'send-notification-message': subscriptions_1.routeRequest(id, { action: note_action_1.default.message, data: { message: data.message.message, notificationId: data.notificationId || null }, id }, ack); break; case 'close-notification': subscriptions_1.routeRequest(id, { action: note_action_1.default.close, data: { notificationId: data.notificationId || null }, id }, ack); break; default: break; } } function unpackGeneralMsg(request) { const { action, data, id } = request.payload; return { action, data, id }; } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const connection_manager_1 = require("../../connection_manager"); const api_protocol_base_js_1 = require("./api_protocol_base.js"); const system_js_1 = require("../../api/system.js"); const log = require("../../log"); const successAck = { success: true }; const ReadRegistryValuePolicyDelegate = { checkPermissions: (args) => { let permitted = false; if (args.payload && args.permissionSettings && args.permissionSettings.enabled === true) { if (Array.isArray(args.permissionSettings.registryKeys)) { let fullPath = args.payload.rootKey; if (args.payload.subkey) { fullPath = fullPath.concat('\\' + args.payload.subkey); } if (args.payload.value) { fullPath = fullPath.concat('\\' + args.payload.value); } permitted = args.permissionSettings.registryKeys.some((specKey) => fullPath.startsWith(specKey)); } } log.writeToLog(1, `ReadRegistryValueDelegate returning ${permitted}`, true); return permitted; } }; exports.SystemApiMap = { 'clear-cache': { apiFunc: clearCache, apiPath: '.clearCache' }, 'create-proxy-socket': createProxySocket, 'authenticate-proxy-socket': authenticateProxySocket, 'convert-options': convertOptions, 'delete-cache-request': deleteCacheRequest, 'download-asset': { apiFunc: downloadAsset, apiPath: '.downloadAsset' }, 'download-preload-scripts': downloadPreloadScripts, 'download-runtime': { apiFunc: downloadRuntime, apiPath: '.downloadRuntime' }, 'exit-desktop': { apiFunc: exitDesktop, apiPath: '.exitDesktop' }, 'flush-cookie-store': { apiFunc: flushCookieStore, apiPath: '.flushCookieStore' }, 'generate-guid': generateGuid, 'get-all-applications': getAllApplications, 'get-all-external-applications': getAllExternalApplications, 'get-all-windows': getAllWindows, 'get-app-asset-info': getAppAssetInfo, 'get-command-line-arguments': { apiFunc: getCommandLineArguments, apiPath: '.getCommandLineArguments' }, 'get-config': { apiFunc: getConfig, apiPath: '.getConfig' }, 'get-crash-reporter-state': getCrashReporterState, 'get-device-id': { apiFunc: getDeviceId, apiPath: '.getDeviceId' }, 'get-device-user-id': getDeviceUserId, 'get-entity-info': getEntityInfo, 'get-environment-variable': { apiFunc: getEnvironmentVariable, apiPath: '.getEnvironmentVariable' }, 'get-focused-window': getFocusedWindow, 'get-host-specs': { apiFunc: getHostSpecs, apiPath: '.getHostSpecs' }, 'get-machine-id': { apiFunc: getMachineId, apiPath: '.getMachineId' }, 'get-min-log-level': getMinLogLevel, 'get-monitor-info': getMonitorInfo, 'get-mouse-position': { apiFunc: getMousePosition, apiPath: '.getMousePosition' }, 'get-nearest-display-root': getNearestDisplayRoot, 'get-proxy-settings': getProxySettings, 'get-remote-config': { apiFunc: getRemoteConfig, apiPath: '.getRemoteConfig' }, 'get-runtime-info': getRuntimeInfo, 'get-rvm-info': getRvmInfo, 'get-preload-scripts': getPreloadScripts, 'get-version': getVersion, 'launch-external-process': { apiFunc: launchExternalProcess, apiPath: '.launchExternalProcess' }, 'list-logs': { apiFunc: listLogs, apiPath: '.getLogList' }, 'monitor-external-process': { apiFunc: monitorExternalProcess, apiPath: '.monitorExternalProcess' }, 'open-url-with-browser': openUrlWithBrowser, 'process-snapshot': processSnapshot, 'raise-event': raiseEvent, 'raise-many-events': raiseManyEvents, 'read-registry-value': { apiFunc: readRegistryValue, apiPath: '.readRegistryValue', apiPolicyDelegate: ReadRegistryValuePolicyDelegate }, 'release-external-process': { apiFunc: releaseExternalProcess, apiPath: '.releaseExternalProcess' }, 'resolve-uuid': resolveUuid, 'resource-fetch-authenticate': { apiFunc: authenticateResourceFetch }, 'get-cookies': { apiFunc: getCookies, apiPath: '.getCookies' }, 'set-cookie': setCookie, 'set-min-log-level': setMinLogLevel, 'show-developer-tools': showDeveloperTools, 'start-crash-reporter': startCrashReporter, 'terminate-external-process': { apiFunc: terminateExternalProcess, apiPath: '.terminateExternalProcess' }, 'update-proxy': updateProxy, 'view-log': { apiFunc: viewLog, apiPath: '.getLog' }, 'write-to-log': writeToLog }; function init() { api_protocol_base_js_1.registerActionMap(exports.SystemApiMap, 'System'); } exports.init = init; function didFail(e) { return e !== undefined && e.constructor === Error; } function readRegistryValue(identity, message, ack) { const dataAck = Object.assign({}, successAck); const { payload: { rootKey, subkey, value } } = message; dataAck.data = system_js_1.System.readRegistryValue(rootKey, subkey, value); ack(dataAck); } function setMinLogLevel(identity, message, ack, nack) { const { payload: { level } } = message; const response = system_js_1.System.setMinLogLevel(level); if (didFail(response)) { nack(response); } else { ack(successAck); } } function getMinLogLevel(identity, message, ack, nack) { const response = system_js_1.System.getMinLogLevel(); if (didFail(response)) { nack(response); } else { const dataAck = Object.assign({}, successAck); dataAck.data = response; ack(dataAck); } } function startCrashReporter(identity, message, ack) { const dataAck = Object.assign({}, successAck); const { payload } = message; dataAck.data = system_js_1.System.startCrashReporter(identity, payload); ack(dataAck); } function getCrashReporterState(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getCrashReporterState(); ack(dataAck); } function getAppAssetInfo(identity, message, ack, nack) { const options = message.payload; system_js_1.System.getAppAssetInfo(identity, options, (data) => { const dataAck = Object.assign({}, successAck); delete data.path; dataAck.data = data; ack(dataAck); }, nack); } function getDeviceUserId(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getDeviceUserId(); ack(dataAck); } function raiseEvent(identity, message, ack) { const { payload: { eventName, eventArgs } } = message; system_js_1.System.raiseEvent(eventName, eventArgs); ack(successAck); } function raiseManyEvents(identity, message) { const { payload } = message; return system_js_1.System.raiseManyEvents(payload); } function convertOptions(identity, message, ack) { const { payload } = message; const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.convertOptions(payload); ack(dataAck); } function generateGuid(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.generateGUID(); ack(dataAck); } function showDeveloperTools(identity, message, ack) { const { payload: { uuid, name } } = message; system_js_1.System.showDeveloperTools(uuid, name); ack(successAck); } function clearCache(identity, message, ack, nack) { const { payload } = message; system_js_1.System.clearCache(identity, payload, (err) => { if (!err) { ack(successAck); } else { nack(err); } }); } function createProxySocket(identity, message, ack, nack) { const { payload } = message; system_js_1.System.createProxySocket(payload, ack, nack); } function authenticateProxySocket(identity, message) { const { payload } = message; system_js_1.System.authenticateProxySocket(payload); } function deleteCacheRequest(identity, message, ack, nack) { system_js_1.System.deleteCacheOnExit(() => ack(successAck), nack); } function exitDesktop(identity, message, ack) { ack(successAck); system_js_1.System.exit(); } function getAllApplications(identity, message, ack) { const { locals } = message; const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getAllApplications(); if (locals && locals.aggregate) { const { aggregate } = locals; dataAck.data = [...dataAck.data, ...aggregate]; } ack(dataAck); } function getAllExternalApplications(identity, message, ack) { const { locals } = message; const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getAllExternalApplications(); if (locals && locals.aggregate) { const { aggregate } = locals; const currentApplication = connection_manager_1.getMeshUuid(); const filteredAggregate = aggregate.filter((result) => (result.uuid !== currentApplication)); const filteredAggregateSet = [...new Set(filteredAggregate)]; dataAck.data = [...dataAck.data, ...filteredAggregateSet]; } ack(dataAck); } function getAllWindows(identity, message, ack) { const { locals } = message; const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getAllWindows(); if (locals && locals.aggregate) { const { aggregate } = locals; dataAck.data = [...dataAck.data, ...aggregate]; } ack(dataAck); } function getCommandLineArguments(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getCommandLineArguments(); ack(dataAck); } function getConfig(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getConfig().data; ack(dataAck); } function getDeviceId(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getDeviceId(); ack(dataAck); } function getEntityInfo(identity, message, ack, nack) { const { payload: { uuid, name } } = message; return system_js_1.System.getEntityInfo({ uuid, name }); } function getFocusedWindow(identity, message, ack) { const dataAck = Object.assign({}, successAck); const { locals } = message; if (locals && locals.aggregate) { const found = locals.aggregate.find((x) => !!x); if (found) { dataAck.data = found; return ack(dataAck); } } dataAck.data = system_js_1.System.getFocusedWindow(); ack(dataAck); } function getRemoteConfig(identity, message, ack, nack) { const { payload: { url } } = message; system_js_1.System.getRemoteConfig(url, (data) => { const dataAck = Object.assign({}, successAck); dataAck.data = data; ack(dataAck); }, nack); } function getEnvironmentVariable(identity, message, ack) { const { payload: { environmentVariables } } = message; const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getEnvironmentVariable(environmentVariables); ack(dataAck); } function viewLog(identity, message, ack, nack) { const { payload: { name = '' } = {} } = message; system_js_1.System.getLog(name, (err, contents) => { if (!err) { const dataAck = Object.assign({}, successAck); dataAck.data = contents; ack(dataAck); } else { nack(err); } }); } function listLogs(identity, message, ack, nack) { system_js_1.System.getLogList((err, logList) => { if (!err) { const dataAck = Object.assign({}, successAck); dataAck.data = logList; ack(dataAck); } else { nack(err); } }); } function getMonitorInfo(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getMonitorInfo(); ack(dataAck); } function getMousePosition(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getMousePosition(); ack(dataAck); } function processSnapshot(identity, message, ack) { const { locals } = message; const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getProcessList(); if (locals && locals.aggregate) { const { aggregate } = locals; const aggregateSet = [...new Set(aggregate)]; dataAck.data = [...dataAck.data, ...aggregateSet]; } ack(dataAck); } function getProxySettings(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getProxySettings(); ack(dataAck); } function getVersion(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getVersion(); ack(dataAck); } function getRuntimeInfo(identity, message, ack, nack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getRuntimeInfo(identity); ack(dataAck); } function getRvmInfo(identity, message, ack, nack) { system_js_1.System.getRvmInfo(identity, (data) => { const dataAck = Object.assign({}, successAck); dataAck.data = data; ack(dataAck); }, nack); } function launchExternalProcess(identity, message, ack, nack) { const { payload } = message; system_js_1.System.launchExternalProcess(identity, payload, (err, res) => { if (!err) { const dataAck = Object.assign({}, successAck); dataAck.data = res; ack(dataAck); } else { nack(err); } }); } function writeToLog(identity, message, ack, nack) { const { payload = {} } = message; const { level: logLevel = '', message: logMessage = '' } = payload; const err = system_js_1.System.log(logLevel, logMessage); if (err) { nack(err); } else { ack(successAck); } } function openUrlWithBrowser(identity, message, ack) { const { payload: { url } } = message; system_js_1.System.openUrlWithBrowser(url); ack(successAck); } function releaseExternalProcess(identity, message, ack) { const { payload: { uuid } } = message; system_js_1.System.releaseExternalProcess(uuid); ack(successAck); } function monitorExternalProcess(identity, message, ack, nack) { const { payload } = message; system_js_1.System.monitorExternalProcess(identity, payload, (data) => { const dataAck = Object.assign({}, successAck); dataAck.data = data; ack(dataAck); }, nack); } function setCookie(identity, message, ack, nack) { const { payload } = message; system_js_1.System.setCookie(payload, () => ack(successAck), nack); } function getCookies(identity, message, ack, nack) { const { payload } = message; system_js_1.System.getCookies(payload, (data) => { const dataAck = Object.assign({}, successAck); dataAck.data = data; ack(dataAck); }, nack); } function flushCookieStore(identity, message, ack, nack) { system_js_1.System.flushCookieStore(() => ack(successAck)); } function terminateExternalProcess(identity, message, ack) { const { payload = {} } = message; const { uuid, timeout, child } = payload; const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.terminateExternalProcess(uuid, timeout, child); ack(dataAck); } function updateProxy(identity, message, ack, nack) { const { payload: { type, proxyAddress, proxyPort } } = message; const err = system_js_1.System.updateProxySettings(type, proxyAddress, proxyPort); if (!err) { ack(successAck); } else { nack(err); } } function getNearestDisplayRoot(identity, message, ack) { const { payload } = message; const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getNearestDisplayRoot(payload); ack(dataAck); } function downloadAsset(identity, message, ack, nack) { const { payload } = message; system_js_1.System.downloadAsset(identity, payload, (err) => { if (!err) { ack(successAck); } else { nack(err); } }); } function downloadPreloadScripts(identity, message, ack, nack) { const { payload: { scripts } } = message; system_js_1.System.downloadPreloadScripts(identity, scripts) .then((downloadResults) => { const dataAck = Object.assign({}, successAck); dataAck.data = downloadResults; ack(dataAck); }) .catch(nack); } function downloadRuntime(identity, message, ack, nack) { const { payload } = message; system_js_1.System.downloadRuntime(identity, payload, (err) => { if (err) { nack(err); } else { ack(successAck); } }); } function getHostSpecs(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getHostSpecs(); ack(dataAck); } function getMachineId(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.getMachineId(); ack(dataAck); } function resolveUuid(identity, message, ack, nack) { const { payload: { entityKey } } = message; system_js_1.System.resolveUuid(identity, entityKey, (err, entity) => { if (err) { nack(err); } else { const dataAck = Object.assign({}, successAck); dataAck.data = entity; ack(dataAck); } }); } function getPreloadScripts(identity, message, ack, nack) { system_js_1.System.getPreloadScripts(identity) .then((preloadScripts) => { const dataAck = Object.assign({}, successAck); dataAck.data = preloadScripts; ack(dataAck); }) .catch(nack); } function authenticateResourceFetch(identity, message, ack) { const { payload } = message; const dataAck = Object.assign({}, successAck); dataAck.data = system_js_1.System.authenticateResourceFetch(identity, payload); ack(dataAck); } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const { Application } = require('../../api/application'); const { Window } = require('../../api/window'); const api_protocol_base_1 = require("./api_protocol_base"); const grouped_window_moves_1 = require("./grouped_window_moves"); const core_state_1 = require("../../core_state"); const successAck = { success: true }; exports.windowApiMap = { 'animate-window': animateWindow, 'blur-window': blurWindow, 'bring-window-to-front': bringWindowToFront, 'close-window': closeWindow, 'disable-window-frame': disableUserMovement, 'dock-window': dockWindow, 'enable-window-frame': enableUserMovement, 'execute-javascript-in-window': { apiFunc: executeJavascript, apiPath: '.executeJavaScript' }, 'flash-window': flashWindow, 'focus-window': focusWindow, 'get-current-window-options': getCurrentWindowOptions, 'get-all-frames': getAllFrames, 'get-window-bounds': getWindowBounds, 'get-window-group': getWindowGroup, 'get-window-info': getWindowInfo, 'get-window-native-id': { apiFunc: getWindowNativeId, apiPath: '.getNativeId' }, 'get-window-options': getWindowOptions, 'get-window-snapshot': { apiFunc: getWindowSnapshot, apiPath: '.getSnapshot' }, 'get-window-state': getWindowState, 'get-zoom-level': getZoomLevel, 'hide-window': hideWindow, 'is-window-showing': isWindowShowing, 'join-window-group': joinWindowGroup, 'leave-window-group': leaveWindowGroup, 'maximize-window': maximizeWindow, 'merge-window-groups': mergeWindowGroups, 'minimize-window': minimizeWindow, 'move-window': moveWindow, 'move-window-by': moveWindowBy, 'navigate-window': navigateWindow, 'navigate-window-back': navigateWindowBack, 'navigate-window-forward': navigateWindowForward, 'stop-window-navigation': stopWindowNavigation, 'register-window-name': registerWindowName, 'reload-window': reloadWindow, 'redirect-window-to-url': redirectWindowToUrl, 'resize-window': resizeWindow, 'resize-window-by': resizeWindowBy, 'restore-window': restoreWindow, 'show-menu': showMenu, 'show-window': showWindow, 'set-foreground-window': setForegroundWindow, 'set-window-bounds': setWindowBounds, 'set-window-preload-state': setWindowPreloadState, 'set-zoom-level': setZoomLevel, 'show-at-window': showAtWindow, 'stop-flash-window': stopFlashWindow, 'undock-window': undockWindow, 'update-window-options': updateWindowOptions, 'window-authenticate': windowAuthenticate, 'window-embedded': windowEmbedded, 'window-exists': windowExists, 'window-get-cached-bounds': getCachedBounds }; function init() { const registerThis = !core_state_1.argo['use-legacy-window-groups'] ? grouped_window_moves_1.hijackMovesForGroupedWindows(exports.windowApiMap) : exports.windowApiMap; api_protocol_base_1.registerActionMap(registerThis, 'Window'); } exports.init = init; function windowAuthenticate(identity, message, ack, nack) { const { payload } = message; const { userName, password } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.authenticate(windowIdentity, userName, password, (err) => { if (!err) { ack(successAck); } else { nack(err); } }); } function redirectWindowToUrl(identity, message, ack) { const { payload } = message; const { targetUuid: uuid, targetName: name, url } = payload; const windowIdentity = { uuid, name }; Window.navigate(windowIdentity, url); ack(successAck); } function updateWindowOptions(identity, rawMessage, ack) { const message = JSON.parse(JSON.stringify(rawMessage)); const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.updateOptions(windowIdentity, payload.options); ack(successAck); } function stopFlashWindow(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.stopFlashing(windowIdentity); ack(successAck); } function setWindowBounds(identity, message, ack) { const { payload } = message; const { top, left, width, height } = payload; const { uuid, name } = api_protocol_base_1.getTargetWindowIdentity(payload); Window.setBounds({ uuid, name }, left, top, width, height); ack(successAck); } function setWindowPreloadState(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(identity); Window.setWindowPreloadState(windowIdentity, payload); ack(successAck); } function setForegroundWindow(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.setAsForeground(windowIdentity); ack(successAck); } function showAtWindow(identity, message, ack) { const { payload } = message; const { force, left, top } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.showAt(windowIdentity, left, top, force); ack(successAck); } function showMenu(identity, message, ack) { const { payload } = message; const { x, y, editable, hasSelectedText } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.showMenu(windowIdentity, x, y, editable, hasSelectedText); ack(successAck); } function showWindow(identity, message, ack) { const { payload } = message; const { force } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.show(windowIdentity, force); ack(successAck); } function restoreWindow(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.restore(windowIdentity); ack(successAck); } function resizeWindow(identity, message, ack) { const { payload } = message; const { width, height, anchor } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.resizeTo(windowIdentity, width, height, anchor); ack(successAck); } function resizeWindowBy(identity, message, ack) { const { payload } = message; const { deltaHeight, deltaWidth, anchor } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.resizeBy(windowIdentity, deltaWidth, deltaHeight, anchor); ack(successAck); } function undockWindow(identity, message, ack) { ack(successAck); } function moveWindow(identity, message, ack) { const { payload } = message; const { top, left } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.moveTo(windowIdentity, left, top); ack(successAck); } function moveWindowBy(identity, message, ack) { const { payload } = message; const { deltaTop, deltaLeft } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.moveBy(windowIdentity, deltaLeft, deltaTop); ack(successAck); } function navigateWindow(identity, message, ack) { const { payload } = message; const { url } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.navigate(windowIdentity, url); ack(successAck); } function navigateWindowBack(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.navigateBack(windowIdentity); ack(successAck); } function navigateWindowForward(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.navigateForward(windowIdentity); ack(successAck); } function stopWindowNavigation(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.stopNavigation(windowIdentity); ack(successAck); } function reloadWindow(identity, message, ack) { const { payload } = message; const { ignoreCache } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.reload(windowIdentity, ignoreCache); ack(successAck); } function minimizeWindow(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.minimize(windowIdentity); ack(successAck); } function mergeWindowGroups(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); const groupingIdentity = api_protocol_base_1.getGroupingWindowIdentity(payload); return Window.mergeGroups(windowIdentity, groupingIdentity).then(() => ack(successAck)); } function maximizeWindow(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.maximize(windowIdentity); ack(successAck); } function leaveWindowGroup(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); return Window.leaveGroup(windowIdentity).then(() => ack(successAck)); } function joinWindowGroup(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); const groupingIdentity = api_protocol_base_1.getGroupingWindowIdentity(payload); return Window.joinGroup(windowIdentity, groupingIdentity).then(() => ack(successAck)); } function isWindowShowing(identity, message, ack) { const { payload } = message; const dataAck = Object.assign({}, successAck); const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); dataAck.data = Window.isShowing(windowIdentity); ack(dataAck); } function hideWindow(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.hide(windowIdentity); ack(successAck); } function getAllFrames(identity, message) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); return Window.getAllFrames(windowIdentity); } function getWindowSnapshot(identity, message) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); return Window.getSnapshot({ identity: windowIdentity, payload }); } function getWindowState(identity, message, ack) { const { payload } = message; const dataAck = Object.assign({}, successAck); const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); dataAck.data = Window.getState(windowIdentity); ack(dataAck); } function getWindowOptions(identity, message, ack) { const { payload } = message; const dataAck = Object.assign({}, successAck); const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); dataAck.data = Window.getOptions(windowIdentity); ack(dataAck); } function getCurrentWindowOptions(identity, message, ack) { const dataAck = Object.assign({}, successAck); dataAck.data = Window.getOptions(identity); ack(dataAck); } function getWindowInfo(identity, message, ack) { const { payload } = message; const dataAck = Object.assign({}, successAck); const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); dataAck.data = Window.getWindowInfo(windowIdentity); ack(dataAck); } function getWindowNativeId(identity, message, ack) { const { payload } = message; const dataAck = Object.assign({}, successAck); const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); dataAck.data = Window.getNativeId(windowIdentity); ack(dataAck); } function getWindowGroup(identity, message, ack) { const { payload } = message; const { crossApp } = payload; const dataAck = Object.assign({}, successAck); const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); const windowGroup = Window.getGroup(windowIdentity); dataAck.data = windowGroup.map((window) => { if (crossApp === true) { return { uuid: window.uuid, name: window.name, windowName: window.name }; } else { return window.name; } }); ack(dataAck); } function getWindowBounds(identity, message, ack) { const { payload } = message; const dataAck = Object.assign({}, successAck); const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); dataAck.data = Window.getBounds(windowIdentity); ack(dataAck); } function focusWindow(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.focus(windowIdentity); ack(successAck); } function flashWindow(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.flash(windowIdentity); ack(successAck); } function enableUserMovement(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.enableUserMovement(windowIdentity); ack(successAck); } function executeJavascript(identity, message, ack, nack) { const { payload } = message; const { code } = payload; const dataAck = Object.assign({}, successAck); const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); let { uuid: pUuid } = windowIdentity; while (pUuid) { if (pUuid === identity.uuid) { return Window.executeJavascript(windowIdentity, code, (err, result) => { if (err) { nack(err); } else { dataAck.data = result; ack(dataAck); } }); } pUuid = Application.getParentApplication({ uuid: pUuid }); } return nack(new Error('Rejected, target window is not owned by requesting identity')); } function disableUserMovement(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.disableUserMovement(identity, windowIdentity); ack(successAck); } function windowEmbedded(identity, message, ack) { const { payload } = message; payload.uuid = payload.targetUuid; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.embed(windowIdentity, `0x${payload.parentHwnd}`); ack(successAck); } function closeWindow(identity, message, ack) { const { payload } = message; const { force } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.close(windowIdentity, force, () => { ack(successAck); }); } function bringWindowToFront(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.bringToFront(windowIdentity); ack(successAck); } function blurWindow(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.blur(windowIdentity); ack(successAck); } function animateWindow(identity, message, ack) { const { payload } = message; const { options, transitions } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.animate(windowIdentity, transitions, options, () => { ack(successAck); }); } function dockWindow(identity, message, ack) { ack(successAck); } function windowExists(identity, message, ack) { const { payload } = message; const dataAck = Object.assign({}, successAck); const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); dataAck.data = Window.exists(windowIdentity); ack(dataAck); } function getCachedBounds(identity, message, ack, nack) { const { payload } = message; const dataAck = Object.assign({}, successAck); const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.getBoundsFromDisk(windowIdentity, (data) => { dataAck.data = data; ack(dataAck); }, nack); } function getZoomLevel(identity, message, ack) { const { payload } = message; const dataAck = Object.assign({}, successAck); const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.getZoomLevel(windowIdentity, (result) => { dataAck.data = result; ack(dataAck); }); } function setZoomLevel(identity, message, ack) { const { payload } = message; const { level } = payload; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.setZoomLevel(windowIdentity, level); ack(successAck); } function registerWindowName(identity, message, ack) { const { payload } = message; const windowIdentity = api_protocol_base_1.getTargetWindowIdentity(payload); Window.registerWindowName(windowIdentity); ack(successAck); } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const application_1 = require("./api_handlers/application"); const external_application_1 = require("./api_handlers/external_application"); const authorization_1 = require("./api_handlers/authorization"); const clipboard_1 = require("./api_handlers/clipboard"); const frame_1 = require("./api_handlers/frame"); const channel_1 = require("./api_handlers/channel"); const global_hotkey_1 = require("./api_handlers/global_hotkey"); const event_listener_1 = require("./api_handlers/event_listener"); const interappbus_1 = require("./api_handlers/interappbus"); const NotificationApiHandler = require('./api_handlers/notifications').NotificationApiHandler; const system_1 = require("./api_handlers/system"); const window_1 = require("./api_handlers/window"); const api_protocol_base_1 = require("./api_handlers/api_protocol_base"); const connection_manager_1 = require("../connection_manager"); const middleware_entity_existence_1 = require("./api_handlers/middleware_entity_existence"); const mesh_middleware_1 = require("./api_handlers/mesh_middleware"); const deprecated_external_windowing_middleware_1 = require("./api_handlers/deprecated_external_windowing_middleware"); middleware_entity_existence_1.registerMiddleware(api_protocol_base_1.getDefaultRequestHandler()); if (deprecated_external_windowing_middleware_1.legacyWindowingEnabled()) { deprecated_external_windowing_middleware_1.registerMiddleware(api_protocol_base_1.getDefaultRequestHandler()); } if (connection_manager_1.meshEnabled) { mesh_middleware_1.registerMiddleware(api_protocol_base_1.getDefaultRequestHandler()); } authorization_1.registerMiddleware(api_protocol_base_1.getDefaultRequestHandler()); function initApiHandlers() { application_1.init(); const externalApplicationApiHandler = new external_application_1.ExternalApplicationApiHandler(); authorization_1.init(); clipboard_1.init(); const frameApiHandler = new frame_1.FrameApiHandler(); const channelApiHandler = new channel_1.ChannelApiHandler(); event_listener_1.init(); interappbus_1.init(); const notificationApiHandler = new NotificationApiHandler(); system_1.init(); const globalHotkeyApiHandler = new global_hotkey_1.GlobalHotkeyApiHandler(); window_1.init(); api_protocol_base_1.init(); const apiPolicyProcessor = require('./api_handlers/api_policy_processor'); } exports.initApiHandlers = initApiHandlers; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const errors = require('../../../common/errors'); class AckMessage { constructor(breadcrumbs, originalAction) { this.action = 'ack'; if (breadcrumbs) { this.breadcrumbs = breadcrumbs; } if (originalAction) { this.originalAction = originalAction; } } addBreadcrumb(name, time, messageId) { this.breadcrumbs = this.breadcrumbs || []; this.breadcrumbs.push({ action: this.originalAction, messageId: messageId || this.correlationId, name, time: time || Date.now() }); } } exports.AckMessage = AckMessage; class AckPayload { constructor(data) { this.data = data; this.success = true; } } exports.AckPayload = AckPayload; class NackPayload { constructor(error) { this.reason = ''; this.error = null; if (typeof error === 'string') { this.reason = error; } else { const errorObject = errors.errorToPOJO(error); this.reason = error.message; this.error = errorObject; } } } exports.NackPayload = NackPayload; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const ack_1 = require("./ack"); class ApiTransportBase { constructor(actionMap, requestHandler) { this.actionMap = actionMap; this.requestHandler = requestHandler; } nackDecorator(ackFunction) { return (err) => { ackFunction(new ack_1.NackPayload(err)); }; } payloadReplacer(key, value) { if (key === 'payload') { return '***masked payload***'; } else { return value; } } passwordReplacer(key, value) { if (key === 'password') { return undefined; } else { return value; } } } exports.ApiTransportBase = ApiTransportBase; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const system = require('../../api/system').System; class RequestHandler { constructor() { this.handlers = []; } mkNext(fn, msg) { return (locals) => { if (locals) { msg.data.locals = msg.data.locals ? Object.assign(msg.data.locals, locals) : locals; } const currFnIdx = this.handlers.indexOf(fn); const handlersLen = this.handlers.length; if (currFnIdx < handlersLen - 1) { const nextHandler = this.handlers[currFnIdx + 1]; nextHandler(msg, this.mkNext(nextHandler, msg)); } else { system.debugLog(1, 'Unhandled Message'); } }; } addHandler(cb) { this.handlers.push(cb); return this; } addPreProcessor(cb) { this.handlers.unshift(cb); return this; } handle(msg) { if (this.handlers.length) { const firstHandler = this.handlers[0]; firstHandler(msg, this.mkNext(firstHandler, msg)); } return this; } } exports.default = RequestHandler; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const ack_1 = require("./ack"); const api_transport_base_1 = require("./api_transport_base"); const coreState = require('../../core_state'); const electronIpc = require('../../transports/electron_ipc'); const system = require('../../api/system').System; class RendererBatchConfiguration { constructor(windowOptions, payload) { const { experimental: { api: { batching: { renderer: { enabled, ttl, maxSize } } } } } = windowOptions; this.enabled = enabled; this.ttl = ttl; this.maxSize = maxSize; this.action = payload && payload.action; this.payload = payload; this.length = (payload && payload.payload && payload.payload.messages && payload.payload.messages.length) || 1; } } class BreadcrumbConfiguration { constructor(windowOptions) { const { experimental: { api: { breadcrumbs } } } = windowOptions; this.enabled = breadcrumbs; } } class ElIPCConfiguration { constructor(breadcrumbConfiguration, rendererBatchConfiguration) { this.breadcrumbConfiguration = breadcrumbConfiguration; this.rendererBatchConfiguration = rendererBatchConfiguration; } } class ElipcStrategy extends api_transport_base_1.ApiTransportBase { constructor(actionMap, requestHandler) { super(actionMap, requestHandler); this.requestHandler.addHandler((mp, next) => { const { identity, data, ack, nack, e, strategyName } = mp; if (strategyName !== this.constructor.name) { next(); } else { const endpoint = this.actionMap[data.action]; if (endpoint) { Promise.resolve() .then(() => endpoint.apiFunc(identity, data, ack, nack)) .then(result => { if (result !== undefined) { ack(new ack_1.AckPayload(result)); } }).catch(err => { nack(err); }); } else { const runtimeVersion = system.getVersion(); const message = `API call ${data.action} not implemented in runtime version: ${runtimeVersion}.`; ack(new ack_1.NackPayload(message)); } } }); } canTrySend(routingInfo) { const { browserWindow, frameRoutingId } = routingInfo; const browserWindowLocated = browserWindow; const browserWindowExists = !browserWindow.isDestroyed(); const validRoutingId = typeof frameRoutingId === 'number'; return browserWindowLocated && browserWindowExists && validRoutingId; } innerSend(payload, frameRoutingId, mainFrameRoutingId, browserWindow) { if (frameRoutingId === mainFrameRoutingId) { if (coreState.argo.framestrategy === 'frames') { browserWindow.webContents.sendToFrame(frameRoutingId, electronIpc.channels.CORE_MESSAGE, payload); } else { browserWindow.send(electronIpc.channels.CORE_MESSAGE, payload); } } else { browserWindow.webContents.sendToFrame(frameRoutingId, electronIpc.channels.CORE_MESSAGE, payload); } } registerMessageHandlers() { electronIpc.ipc.on(electronIpc.channels.WINDOW_MESSAGE, this.onMessage.bind(this)); } send(identity, payloadObj) { const { uuid, name } = identity; const routingInfo = coreState.getRoutingInfoByUuidFrame(uuid, name); if (!routingInfo) { system.debugLog(1, `Routing info for uuid:${uuid} name:${name} not found`); return; } const { browserWindow, mainFrameRoutingId, frameRoutingId } = routingInfo; const payload = JSON.stringify(payloadObj); if (!this.canTrySend(routingInfo)) { system.debugLog(1, `uuid:${uuid} name:${name} frameRoutingId:${frameRoutingId} not reachable, payload:${payload}`); } else { this.innerSend(payload, frameRoutingId, mainFrameRoutingId, browserWindow); } } onClientAuthenticated(cb) { throw new Error('Not implemented'); } onClientDisconnect(cb) { throw new Error('Not implemented'); } onMessage(e, rawData, ackFactoryDelegate) { try { const browserWindow = e.sender.getOwnerBrowserWindow(); const currWindow = browserWindow ? coreState.getWinById(browserWindow.id) : null; const openfinWindow = currWindow && currWindow.openfinWindow; const opts = openfinWindow && openfinWindow._options; if (!opts) { throw new Error(`Unable to locate window information for endpoint with window id ${browserWindow.id}`); } const data = JSON.parse(JSON.stringify(rawData)); const configuration = new ElIPCConfiguration(new BreadcrumbConfiguration(opts), new RendererBatchConfiguration(opts, data)); const ackFactory = ackFactoryDelegate || this.ackDecorator.bind(this); const ack = !data.isSync ? ackFactory(e, data.messageId, data, configuration) : this.ackDecoratorSync(e, data.messageId); const nack = this.nackDecorator(ack); const entityType = e.sender.getEntityType(e.frameRoutingId); const isWindow = !e.sender.isIframe(e.frameRoutingId); const { api: { iframe: { enableDeprecatedSharedName } } } = opts; let subFrameName; if (isWindow || enableDeprecatedSharedName) { subFrameName = opts.name; } else { subFrameName = e.sender.getFrameName(e.frameRoutingId); } const identity = { batch: data.action === 'api-batch', entityType, name: subFrameName, parentFrame: opts.name, uuid: opts.uuid }; const disableIabSecureLogging = coreState.getAppObjByUuid(opts.uuid)._options.disableIabSecureLogging; let replacer = (!disableIabSecureLogging && (data.action === 'publish-message' || data.action === 'send-message')) ? this.payloadReplacer : null; if (data.action === 'window-authenticate') { replacer = this.passwordReplacer; } system.debugLog(1, `received in-runtime${data.isSync ? '-sync ' : ''}: ${e.frameRoutingId} [${identity.uuid}]-[${identity.name}] ${JSON.stringify(data, replacer)}`); if (!identity.batch) { this.requestHandler.handle({ identity, data, ack, nack, e, strategyName: this.constructor.name }); } else { const deferredAckFactory = this.ackDeferredDecorator(e, data.messageId, data, configuration); data.payload.messages.forEach((m) => { this.onMessage(e, m, deferredAckFactory); }); } } catch (err) { system.debugLog(1, err); } } ackDecoratorSync(e, messageId) { const ackObj = new ack_1.AckMessage(); ackObj.correlationId = messageId; return (payload) => { ackObj.payload = payload; try { system.debugLog(1, `sent sync in-runtime <= ${JSON.stringify(ackObj)}`); } catch (err) { } if (!e.sender.isDestroyed()) { e.returnValue = JSON.stringify(ackObj); } }; } createAckObject(configuration, originalPayload, messageId) { const usingBreadcrumbs = configuration.breadcrumbConfiguration.enabled; const ackObj = (!usingBreadcrumbs ? new ack_1.AckMessage() : new ack_1.AckMessage(originalPayload.breadcrumb, originalPayload.action)); ackObj.correlationId = messageId; return ackObj; } ackDecorator(e, messageId, originalPayload, baseConfiguration) { const configuration = baseConfiguration; const usingBreadcrumbs = configuration.breadcrumbConfiguration.enabled; const ackObj = this.createAckObject(configuration, originalPayload, messageId); if (usingBreadcrumbs) { ackObj.addBreadcrumb('core/ackDecorator'); } return (payload) => { ackObj.payload = payload; try { system.debugLog(1, `sent in-runtime <= ${e.frameRoutingId} ${JSON.stringify(ackObj)}`); } catch (err) { } if (!e.sender.isDestroyed()) { if (usingBreadcrumbs) { ackObj.addBreadcrumb('core/ACK'); } e.sender.sendToFrame(e.frameRoutingId, electronIpc.channels.CORE_MESSAGE, JSON.stringify(ackObj)); } }; } ackDeferredDecorator(e, messageId, originalPayload, baseConfiguration) { const configuration = baseConfiguration; const usingBreadcrumbs = configuration.breadcrumbConfiguration.enabled; const deferredAcks = []; const mainAck = this.ackDecorator(e, messageId, originalPayload, baseConfiguration); return (e, messageId, originalPayload) => { const ackObj = this.createAckObject(configuration, originalPayload, messageId); if (usingBreadcrumbs) { ackObj.addBreadcrumb('core/ackDelegate'); } return (payload) => { ackObj.payload = payload; if (usingBreadcrumbs) { ackObj.addBreadcrumb('core/deferredACK'); } deferredAcks.push(ackObj); if (deferredAcks.length === configuration.rendererBatchConfiguration.length) { mainAck({ success: true, data: deferredAcks }); } }; }; } } exports.ElipcStrategy = ElipcStrategy; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const ack_1 = require("./ack"); const api_transport_base_1 = require("./api_transport_base"); const route_1 = require("../../../common/route"); const log = require("../../log"); const external_application_1 = require("../../api/external_application"); const socketServer = require('../../transports/socket_server').server; const system = require('../../api/system').System; class WebSocketStrategy extends api_transport_base_1.ApiTransportBase { constructor(actionMap, requestHandler) { super(actionMap, requestHandler); this.requestHandler.addHandler((mp, next) => { const { identity, data, ack, nack, strategyName } = mp; if (strategyName !== this.constructor.name) { next(); } else { const endpoint = actionMap[data.action]; if (endpoint) { Promise.resolve() .then(() => endpoint.apiFunc(identity, data, ack, nack)) .then(result => { if (result !== undefined) { ack(new ack_1.AckPayload(result)); } }).catch(err => { ack(new ack_1.NackPayload(err)); }); } else { const runtimeVersion = system.getVersion(); const nackMessage = `API call ${data.action} not implemented in runtime version: ${runtimeVersion}.`; ack(new ack_1.NackPayload(nackMessage)); } } }); } registerMessageHandlers() { socketServer.on(route_1.default.connection('message'), this.onMessage.bind(this)); } send(externalConnection, payload) { const { id } = externalConnection; const message = JSON.stringify(payload); if (socketServer.isConnectionOpen(id)) { log.writeToLog('info', `sent external-adapter <= ${id} ${message}`); socketServer.send(id, message); } else { log.writeToLog('info', `Socket connection is not open, therefore not sending message to external adapter <= ${id} ${message}`); } } onClientAuthenticated(cb) { socketServer.on(route_1.default.connection('authenticated'), cb); } onClientDisconnect(cb) { socketServer.on(route_1.default.connection('close'), cb); } onMessage(id, data) { const ack = this.ackDecorator(id, data.messageId); const nack = this.nackDecorator(ack); const requestingConnection = external_application_1.ExternalApplication.getExternalConnectionById(id); let identity = null; if (requestingConnection) { if (data.requestingIdentity) { identity = data.requestingIdentity; identity.runtimeUuid = requestingConnection.uuid; } else { identity = { uuid: requestingConnection.uuid, name: requestingConnection.uuid }; } } const replacer = (data.action === 'publish-message' || data.action === 'send-message') ? this.payloadReplacer : null; system.debugLog(1, `received external-adapter <= ${id} ${JSON.stringify(data, replacer)}`); this.requestHandler.handle({ data, ack, nack, identity: identity || id, strategyName: this.constructor.name }); } ackDecorator(id, messageId) { const ackObj = new ack_1.AckMessage(); return (payload) => { ackObj.payload = payload; ackObj.correlationId = messageId; if (!socketServer.isConnectionOpen(id)) { system.debugLog(1, `Aborted trying to send a response to external-adapter (ID: ${id}). ` + `Message was going to send: ${JSON.stringify(ackObj)}`); return; } system.debugLog(1, `sent external-adapter <= ${id} ${JSON.stringify(ackObj)}`); socketServer.send(id, JSON.stringify(ackObj)); }; } ackDecoratorSync(e, messageId) { throw new Error('Not implemented'); } } exports.WebSocketStrategy = WebSocketStrategy; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); let path = require('path'); let electron = require('electron'); let queryString = require('querystring'); let BrowserWindow = electron.BrowserWindow; let electronApp = electron.app; let dialog = electron.dialog; let globalShortcut = electron.globalShortcut; let nativeImage = electron.nativeImage; let ProcessInfo = electron.processInfo; let Tray = electron.Tray; let _ = require('underscore'); let System = require('./system.js').System; const window_1 = require("./window"); let convertOpts = require('../convert_options.js'); let coreState = require('../core_state.js'); let externalApiBase = require('../api_protocol/api_handlers/api_protocol_base'); const cached_resource_fetcher_1 = require("../cached_resource_fetcher"); const of_events_1 = require("../of_events"); const window_groups_1 = require("../window_groups"); const utils_1 = require("../rvm/utils"); const navigation_validation_1 = require("../navigation_validation"); const log = require("../log"); const subscription_manager_1 = require("../subscription_manager"); const route_1 = require("../../common/route"); const main_1 = require("../../common/main"); const errors_1 = require("../../common/errors"); const window_groups_runtime_proxy_1 = require("../window_groups_runtime_proxy"); const subscriptionManager = new subscription_manager_1.default(); const TRAY_ICON_KEY = 'tray-icon-events'; let runtimeIsClosing = false; let hasPlugins = false; let rvmBus; let MonitorInfo; var Application = {}; let fetchingIcon = {}; let registeredUsersByApp = {}; electronApp.on('use-plugins-requested', event => { if (hasPlugins) { event.preventDefault(); } }); electronApp.on('ready', function () { rvmBus = require('../rvm/rvm_message_bus').rvmMessageBus; log.writeToLog(1, 'RVM MESSAGE BUS READY', true); MonitorInfo = require('../monitor_info.js'); rvmBus.on(route_1.default.rvmMessageBus('broadcast', 'application', 'manifest-changed'), payload => { const manifests = payload && payload.manifests; if (manifests) { _.each(manifests, manifestObject => { var sourceUrl = manifestObject.sourceUrl; var json = manifestObject.json; var uuid = coreState.getUuidBySourceUrl(sourceUrl); if (uuid) { of_events_1.default.emit(route_1.default.application('manifest-changed', uuid), sourceUrl, json); } else { log.writeToLog(1, `Received manifest-changed event from RVM, unable to determine uuid from source url though: ${sourceUrl}`, true); } }); } else { log.writeToLog(1, `Received manifest-changed event from RVM with invalid data object: ${payload}`, true); } }); rvmBus.on(route_1.default.rvmMessageBus('broadcast', 'application', 'close-app'), payload => { try { const { uuid } = payload; log.writeToLog('info', `received an RVM request to close application with uuid ${uuid}`); if (uuid) { Application.close({ uuid }, true); rvmBus.sendCloseAppRequested(payload); } } catch (err) { log.writeToLog('info', 'error processing RVM Request to close-app'); log.writeToLog('info', err); rvmBus.sendCloseAppError(payload, err).catch(e => { log.writeToLog('info', 'error sending close app response to RVM'); log.writeToLog('info', err); }); } }); }); Application.create = function (opts, configUrl = '', parentIdentity = {}) { let appUrl = opts.url; const { uuid, name } = opts; const initialAppOptions = Object.assign({}, opts); if (appUrl === undefined && opts.mainWindowOptions) { appUrl = opts.mainWindowOptions.url; } const isValidUrl = appUrl === undefined || typeof appUrl === 'string'; if (!isValidUrl) { throw new Error(`Invalid application URL: ${appUrl}`); } const isValidUuid = isNonEmptyString(uuid) && uuid !== '*'; if (!isValidUuid) { throw new Error(`Invalid application UUID: ${uuid}`); } const isValidName = isNonEmptyString(name) && name !== '*'; if (!isValidName) { throw new Error(`Invalid application name: ${name}`); } const isAppRunning = coreState.getAppRunningState(uuid) || coreState.getExternalAppObjByUuid(uuid); if (isAppRunning) { throw new Error(`Application with specified UUID already exists: ${uuid}`); } const parentUuid = parentIdentity && parentIdentity.uuid; if (!navigation_validation_1.validateNavigationRules(uuid, appUrl, parentUuid, opts)) { throw new Error(`Application with specified URL is not allowed: ${opts.appUrl}`); } const existingApp = coreState.appByUuid(uuid); if (existingApp) { coreState.removeApp(existingApp.id); } const appObj = createAppObj(uuid, opts, configUrl); const app = coreState.appByUuid(opts.uuid); if (parentIdentity && parentIdentity.uuid) { app.parentUuid = parentUuid; } app.initialAppOptions = initialAppOptions; return appObj; }; Application.getCurrent = function () { }; Application.getCurrentApplication = function () { console.warn('Deprecated. Please use getCurrent'); }; Application.wrap = coreState.getAppObjByUuid; Application.addEventListener = function (identity, appEvent, listener) { let uuid = identity.uuid; let eventString = route_1.default.application(appEvent, uuid); let errRegex = /^Attempting to call a function in a renderer window that has been closed or released/; let unsubscribe, safeListener, browserWinIsDead; safeListener = (...args) => { try { listener.call(null, ...args); } catch (err) { browserWinIsDead = errRegex.test(err.message); if (browserWinIsDead) { of_events_1.default.removeListener(eventString, safeListener); } } }; of_events_1.default.on(eventString, safeListener); unsubscribe = () => { of_events_1.default.removeListener(eventString, safeListener); }; return unsubscribe; }; function closeChildWins(identity) { Application.getChildWindows(identity).forEach(function (childWindow) { const childWindowIdentity = { name: childWindow.name, uuid: childWindow.uuid }; window_1.Window.close(childWindowIdentity, true); }); } Application.close = function (identity, force, callback) { let app = Application.wrap(identity.uuid); if (!app) { log.writeToLog(1, `Could not close app ${identity.uuid}`, true); if (typeof callback === 'function') { callback(); } return; } let mainWin = app.mainWindow; if (force) { closeChildWins(identity); } if (mainWin) { const mainWindowIdentity = { name: app._options.uuid, uuid: app._options.uuid }; window_1.Window.close(mainWindowIdentity, force, callback); } }; Application.getChildWindows = function (identity) { const uuid = identity.uuid; const appError = checkApplicationAvailability(uuid); if (appError) { throw new Error(appError); } else { const app = Application.wrap(uuid); return coreState.getChildrenByApp(app.id); } }; Application.getGroups = function () { return window_groups_1.default.getGroups(); }; Application.getManifest = function (identity, manifestUrl, callback, errCallback) { if (!manifestUrl) { const appObject = coreState.getAppObjByUuid(identity.uuid); manifestUrl = appObject && appObject._configUrl; } if (manifestUrl) { cached_resource_fetcher_1.fetchReadFile(manifestUrl, true) .then(callback) .catch(errCallback); } else { errCallback(new Error('App not started from manifest')); } }; Application.getParentApplication = function (identity) { const app = coreState.appByUuid(identity.uuid); const { parentUuid } = app || {}; return parentUuid; }; Application.getZoomLevel = function (identity, callback) { const uuid = identity.uuid; const appError = checkApplicationAvailability(uuid); if (appError) { throw new Error(appError); } else { const app = coreState.appByUuid(uuid); window_1.Window.getZoomLevel(app.appObj.identity, callback); } }; Application.getShortcuts = function (identity, callback, errorCallback) { let app = Application.wrap(identity.uuid); let manifestUrl = app && app._configUrl; if (!manifestUrl) { return errorCallback(new Error('App must be started from a manifest to be able to request its shortcut configuration')); } utils_1.sendToRVM({ topic: 'application', action: 'get-shortcut-state', sourceUrl: manifestUrl }).then(callback, errorCallback) .catch(errorCallback); }; Application.getInfo = function (identity, callback) { const app = coreState.appByUuid(identity.uuid); const manifestObj = coreState.getClosestManifest(identity); const { url, manifest } = manifestObj || {}; const parentUuid = Application.getParentApplication(identity); const launchMode = app && app.appObj && app.appObj.launchMode; const response = { initialOptions: app.initialAppOptions, launchMode, manifestUrl: url, manifest, parentUuid, runtime: { version: System.getRuntimeInfo(identity).version } }; callback(response); }; Application.getWindow = function (identity) { let uuid = identity.uuid; return window_1.Window.wrap(uuid, uuid); }; Application.grantAccess = function () { console.warn('Deprecated'); }; Application.grantWindowAccess = function () { console.warn('Deprecated'); }; Application.isRunning = function (identity) { let uuid = identity && identity.uuid; return !!(uuid && coreState.getAppRunningState(uuid) && !coreState.getAppRestartingState(uuid)); }; Application.pingChildWindow = function () { console.warn('Deprecated'); }; Application.registerUser = function (identity, userName, appName, callback, errorCallback) { const uuid = identity.uuid; const app = coreState.getAppByUuid(uuid) || coreState.getExternalAppObjByUuid(uuid); if (!app) { errorCallback(new Error(`application with uuid ${uuid} does not exist`)); return; } const licenseKey = app.licenseKey; const configUrl = coreState.getConfigUrlByUuid(uuid); if (!licenseKey) { errorCallback(new Error(`application with uuid ${uuid} has no licenseKey specified`)); } else if (!configUrl) { errorCallback(new Error(`application with uuid ${uuid} has no _configUrl specified`)); } else if (!rvmBus) { errorCallback(new Error('cannot connect to the RVM')); } else if (!userName) { errorCallback(new Error('\'userId\' field is required to register user')); } else if (!appName) { errorCallback(new Error('\'appName\' field is required to register user')); } else if (userName.length > 128) { errorCallback(new Error('\'userName\' is too long; must be <= 128 characters')); } else if (appName.length > 32) { errorCallback(new Error('\'appName\' is too long; must be <= 32 characters')); } else if (uuid in registeredUsersByApp && registeredUsersByApp[uuid].has(userName)) { errorCallback(new Error(`userName ${userName} is already registered for appName ${appName} with app uuid ${uuid}`)); } else { if (!(uuid in registeredUsersByApp)) { registeredUsersByApp[uuid] = new Set(); } registeredUsersByApp[uuid].add(userName); utils_1.sendToRVM({ topic: 'application', action: 'register-user', sourceUrl: configUrl, runtimeVersion: System.getVersion(), payload: { userName: userName, appName: appName } }).then(callback, errorCallback) .catch(errorCallback); } }; Application.removeEventListener = function (identity, type, listener) { var app = Application.wrap(identity.uuid); of_events_1.default.removeListener(route_1.default.application(type, app.id), listener); }; Application.removeTrayIcon = function (identity) { const app = Application.wrap(identity.uuid); removeTrayIcon(app); }; Application.restart = function (identity) { const uuid = identity.uuid; const appError = checkApplicationAvailability(uuid); if (appError) { throw new Error(appError); } const appObj = coreState.getAppObjByUuid(uuid); coreState.setAppRestartingState(uuid, true); try { Application.close(identity, true, () => { Application.run(identity, appObj._configUrl); of_events_1.default.once(route_1.default.application('initialized', uuid), function () { coreState.setAppRestartingState(uuid, false); }); }); } catch (err) { coreState.setAppRestartingState(uuid, false); console.error(`Error restarting app <${uuid}>`); console.error(err.stack); throw err; } }; Application.revokeAccess = function () { console.warn('Deprecated'); }; Application.revokeWindowAccess = function () { console.warn('Deprecated'); }; Application.run = function (identity, configUrl = '', userAppConfigArgs = undefined) { if (!identity) { return; } const app = createAppObj(identity.uuid, null, configUrl); const mainWindowOpts = convertOpts.convertToElectron(app._options); run(identity, mainWindowOpts, userAppConfigArgs); }; function run(identity, mainWindowOpts, userAppConfigArgs) { const windowIdentity = main_1.getIdentityFromObject(mainWindowOpts); const uuid = identity.uuid; const appWasAlreadyRunning = coreState.getAppRunningState(uuid); const app = Application.wrap(uuid); const appState = coreState.appByUuid(uuid); let sourceUrl = appState.appObj._configUrl; const hideSplashTopic = route_1.default.application('hide-splashscreen', uuid); const eventListenerStrings = []; const hideSplashListener = () => { let rvmPayload = { topic: 'application', action: 'hide-splashscreen', sourceUrl }; if (rvmBus) { rvmBus.publish(rvmPayload); } }; const genLicensePayload = () => { let licenseKey = mainWindowOpts.licenseKey; licenseKey = licenseKey || coreState.getLicenseKey({ uuid }); licenseKey = licenseKey || coreState.getLicenseKey({ uuid: appState.parentUuid }); coreState.setLicenseKey({ uuid }, licenseKey); const parentUuid = appState.parentUuid || null; let parentConfigUrl = null; if (parentUuid) { parentConfigUrl = coreState.getConfigUrlByUuid(parentUuid); } return { licenseKey, uuid, client: { type: 'js' }, parentApp: { uuid: parentUuid, configUrl: parentConfigUrl } }; }; const appEventsForRVM = ['closed', 'ready', 'run-requested', 'crashed', 'error', 'not-responding']; const appStartedHandler = () => { rvmBus.registerLicenseInfo({ data: genLicensePayload() }, sourceUrl); }; const sendAppsEventsToRVMListener = (appEvent) => { if (!sourceUrl) { return; } let type = appEvent.type, rvmPayload = { topic: 'application-event', type, sourceUrl }; if (type === 'ready' || type === 'run-requested') { rvmPayload.hideSplashScreenSupported = true; } else if (type === 'closed') { if (appState.isRestarting) { return; } rvmPayload.isClosing = coreState.shouldCloseRuntime([uuid]); } if (rvmBus) { rvmBus.publish(rvmPayload); } }; let argo = coreState.argo; if (sourceUrl === argo['local-startup-url']) { sourceUrl = argo['startup-url'] || argo['config']; } if (appWasAlreadyRunning) { if (coreState.sentFirstHideSplashScreen(uuid)) { Application.emitHideSplashScreen(identity); } Application.emitRunRequested(identity, queryString.parse(userAppConfigArgs)); return; } of_events_1.default.on(hideSplashTopic, hideSplashListener); of_events_1.default.on(route_1.default.application('started', uuid), appStartedHandler); appEventsForRVM.forEach(appEvent => { of_events_1.default.on(route_1.default.application(appEvent, uuid), sendAppsEventsToRVMListener); }); mainWindowOpts = Object.assign({}, mainWindowOpts, { name: uuid }); const win = window_1.Window.create(app.id, mainWindowOpts); coreState.setWindowObj(app.id, win); app.mainWindow.webContents.once('dom-ready', () => { if (!app.mainWindow.isDestroyed()) { const pid = app.mainWindow.webContents.processId; if (pid) { app._processInfo = new ProcessInfo(pid); app._processInfo.getCpuUsage(); } of_events_1.default.emit(route_1.default.application('connected', uuid), { topic: 'application', type: 'connected', uuid }); } else { const constructorCallbackMessage = { success: false, data: { message: 'Window closed before web context initiatiated' } }; of_events_1.default.emit(route_1.default.window('fire-constructor-callback', uuid, uuid), constructorCallbackMessage); } }); hasPlugins = convertOpts.convertToElectron(mainWindowOpts).webPreferences.plugins; hasPlugins = false; app.mainWindow.on('newListener', (eventString) => { eventListenerStrings.push(eventString); }); of_events_1.default.once(route_1.default.window('closed', uuid, uuid), () => { delete fetchingIcon[uuid]; removeTrayIcon(app); if (uuid in registeredUsersByApp) { delete registeredUsersByApp[uuid]; } of_events_1.default.emit(route_1.default.application('closed', uuid), { topic: 'application', type: 'closed', uuid }); eventListenerStrings.forEach(eventString => { app.mainWindow.removeAllListeners(eventString); }); eventListenerStrings.length = 0; coreState.setAppRunningState(uuid, false); coreState.setSentFirstHideSplashScreen(uuid, false); of_events_1.default.removeAllListeners(hideSplashTopic); appEventsForRVM.forEach(appEvent => { of_events_1.default.removeListener(route_1.default.application(appEvent, uuid), sendAppsEventsToRVMListener); }); of_events_1.default.removeListener(route_1.default.application('started', uuid), appStartedHandler); coreState.removeApp(app.id); const shouldCloseRuntime = app._options._type !== errors_1.ERROR_BOX_TYPES.RENDERER_CRASH && !app._options._runtimeAuthDialog && !runtimeIsClosing && coreState.shouldCloseRuntime() && process.platform !== 'darwin'; if (shouldCloseRuntime) { try { runtimeIsClosing = true; let appsToClose = coreState.getAllAppObjects(); for (var i = appsToClose.length - 1; i >= 0; i--) { let a = appsToClose[i]; if (a.uuid !== app.uuid) { Application.close(a.identity, true); } } window_groups_runtime_proxy_1.deregisterAllRuntimeProxyWindows(); BrowserWindow.getAllWindows().forEach(function (window) { window.close(); }); globalShortcut.unregisterAll(); } catch (err) { console.error('Error shutting down runtime'); console.error(err); console.error(err.stack); } finally { electronApp.exit(0); } } }); const { preloadScripts } = mainWindowOpts; const loadUrl = () => { app.mainWindow.loadURL(app._options.url); coreState.setAppRunningState(uuid, true); of_events_1.default.emit(route_1.default.application('started', uuid), { topic: 'application', type: 'started', uuid }); }; if (main_1.isValidChromePageUrl(app._options.url) || appWasAlreadyRunning) { loadUrl(); app.mainWindow.show(); } else { System.downloadPreloadScripts(windowIdentity, preloadScripts) .catch((error) => { log.writeToLog(1, 'Error while downloading preload scripts for identity ' + `${uuid}-${name} on app run. Will proceed running the app. ` + `Error received: ${error}`, true); }) .then(loadUrl); } } Application.runWithRVM = function (identity, manifestUrl) { return utils_1.sendToRVM({ topic: 'application', action: 'launch-app', sourceUrl: coreState.getConfigUrlByUuid(identity.uuid), data: { configUrl: manifestUrl } }); }; Application.send = function () { console.warn('Deprecated. Please use InterAppBus'); }; Application.setShortcuts = function (identity, config, callback, errorCallback) { let app = Application.wrap(identity.uuid); let manifestUrl = app && app._configUrl; if (manifestUrl) { const options = { topic: 'application', action: 'set-shortcut-state', sourceUrl: manifestUrl, data: config }; utils_1.sendToRVM(options) .then(callback, errorCallback) .catch(errorCallback); } else { errorCallback(new Error('App must be started from a manifest to be able to change its shortcut configuration')); } }; Application.setAppLogUsername = function (identity, username) { let app = Application.wrap(identity.uuid); const options = { topic: 'application', action: 'application-log-username', sourceUrl: app._configUrl, data: { 'userName': username } }; return utils_1.sendToRVM(options); }; Application.setTrayIcon = function (identity, iconUrl, callback, errorCallback) { let { uuid } = identity; if (fetchingIcon[uuid]) { errorCallback(new Error('currently fetching icon')); return; } fetchingIcon[uuid] = true; const appError = checkApplicationAvailability(uuid); if (appError) { errorCallback(new Error(appError)); } const app = Application.wrap(uuid); removeTrayIcon(app); const mainWindowIdentity = app.identity; iconUrl = window_1.Window.getAbsolutePath(mainWindowIdentity, iconUrl); cached_resource_fetcher_1.cachedFetch(mainWindowIdentity, iconUrl, (error, iconFilepath) => { if (!error) { if (app) { const iconImage = nativeImage.createFromPath(iconFilepath); const icon = app.tray = new Tray(iconImage); const monitorInfo = MonitorInfo.getInfo('system-query'); const clickedRoute = route_1.default.application('tray-icon-clicked', app.uuid); const getData = (bounds, source) => { const data = { x: bounds.x, y: bounds.y, bounds, monitorInfo }; return Object.assign(data, source); }; const makeClickHandler = (button) => { return (event, bounds) => { of_events_1.default.emit(clickedRoute, getData(bounds, { button })); }; }; const hoverHandler = (event, bounds) => { of_events_1.default.emit(route_1.default.application('tray-icon-hovering', app.uuid), getData(bounds)); }; const listenerSignatures = [ ['hover', hoverHandler], ['click', makeClickHandler(0)], ['middle-click', makeClickHandler(1)], ['right-click', makeClickHandler(2)] ]; listenerSignatures.forEach(signature => icon.on.apply(icon, signature)); const unsubscribe = () => { listenerSignatures.forEach(signature => icon.removeListener.apply(icon, signature)); }; subscriptionManager.registerSubscription(unsubscribe, app.identity, TRAY_ICON_KEY); if (typeof callback === 'function') { callback(); } } } else { if (typeof errorCallback === 'function') { errorCallback(error); } } fetchingIcon[uuid] = false; }); }; Application.setZoomLevel = function (identity, level) { const uuid = identity.uuid; const appError = checkApplicationAvailability(uuid); if (appError) { throw new Error(appError); } else { const app = coreState.appByUuid(uuid); app.children.forEach(function (childWindow) { const childWindowIdentity = { name: childWindow.openfinWindow.name, uuid: childWindow.openfinWindow.uuid }; window_1.Window.setZoomLevel(childWindowIdentity, level); }); } }; Application.sendApplicationLog = function (identity) { let app = Application.wrap(identity.uuid); const options = { topic: 'application', action: 'application-log-send', sourceUrl: app._configUrl }; return utils_1.sendToRVM(options); }; Application.getTrayIconInfo = function (identity, callback, errorCallback) { const app = Application.wrap(identity.uuid); const bounds = app && app.tray && app.tray.getIconRect(); if (bounds) { callback({ x: bounds.x, y: bounds.y, monitorInfo: MonitorInfo.getInfo('system-query'), bounds }); } else { errorCallback(new Error('cannot get tray icon rect')); } }; Application.scheduleRestart = function (identity, callback, errorCallback) { const uuid = identity.uuid; const appError = checkApplicationAvailability(uuid); if (appError) { errorCallback(new Error(appError)); } else if (!rvmBus) { errorCallback(new Error('cannot connect to the RVM')); } else { const app = Application.wrap(uuid); const success = rvmBus.publish({ topic: 'application', action: 'relaunch-on-close', sourceUrl: app._configUrl, runtimeVersion: System.getVersion() }); if (success) { callback(); } else { errorCallback(new Error('there was an issue sending a message to the RVM')); } } }; Application.terminate = function (identity, callback) { Application.close(identity, true, callback); }; Application.emitHideSplashScreen = function (identity) { var uuid = identity && identity.uuid; if (uuid) { of_events_1.default.emit(route_1.default.application('hide-splashscreen', uuid)); } }; Application.emitRunRequested = function (identity, userAppConfigArgs) { const uuid = identity && identity.uuid; if (uuid) { of_events_1.default.emit(route_1.default.application('run-requested', uuid), { topic: 'application', type: 'run-requested', uuid, userAppConfigArgs }); } }; Application.wait = function () { console.warn('Awaiting native implementation'); }; var appLoadedListeners = {}; var appConnectedListeners = {}; function registerAppLoadedListener(targetIdentity, listenerIdentity) { let targetKey = `${targetIdentity.uuid}-${targetIdentity.name}`; let listenerKey = `${listenerIdentity.uuid}-${listenerIdentity.name}`; let listeners = appLoadedListeners[targetKey] || {}; listeners[listenerKey] = listenerIdentity; appLoadedListeners[targetKey] = listeners; } function registerAppConnectedListener(targetIdentity, listenerIdentity) { let targetKey = `${targetIdentity.uuid}-${targetIdentity.name}`; let listenerKey = `${listenerIdentity.uuid}-${listenerIdentity.name}`; let listeners = appConnectedListeners[targetKey] || {}; listeners[listenerKey] = listenerIdentity; appConnectedListeners[targetKey] = listeners; } function broadcastAppLoaded(targetIdentity) { if (targetIdentity && targetIdentity.uuid && targetIdentity.name) { let targetKey = `${targetIdentity.uuid}-${targetIdentity.name}`; let listeners = appLoadedListeners[targetKey]; if (listeners) { let loadedMessage = { action: 'app-loaded', payload: { appUuid: targetIdentity.uuid, uuid: targetIdentity.uuid + targetIdentity.name, name: targetIdentity.name } }; _.each(listeners, listener => { externalApiBase.sendToIdentity(listener, loadedMessage); }); } } } function broadcastOnAppConnected(targetIdentity) { if (targetIdentity && targetIdentity.uuid && targetIdentity.name) { let targetKey = `${targetIdentity.uuid}-${targetIdentity.name}`; let listeners = appConnectedListeners[targetKey]; if (listeners) { let connectedMessage = { action: 'app-connected', payload: { appUuid: targetIdentity.uuid, uuid: targetIdentity.uuid + targetIdentity.name, name: targetIdentity.name } }; _.each(listeners, listener => { externalApiBase.sendToIdentity(listener, connectedMessage); }); } } } of_events_1.default.on(route_1.default.window('dom-content-loaded', '*'), payload => { broadcastAppLoaded(payload.data[0]); }); of_events_1.default.on(route_1.default.window('connected', '*'), payload => { broadcastOnAppConnected(payload.data[0]); }); Application.notifyOnContentLoaded = function (target, identity) { registerAppLoadedListener(target, identity); console.warn('Deprecated. Please addEventListener'); }; Application.notifyOnAppConnected = function (target, identity) { registerAppConnectedListener(target, identity); console.warn('Deprecated. Please addEventListener'); }; function removeTrayIcon(app) { if (app && app.tray) { try { app.tray.destroy(); app.tray = null; subscriptionManager.removeSubscription(app.identity, TRAY_ICON_KEY); } catch (e) { log.writeToLog(1, e, true); } } } function createAppObj(uuid, opts, configUrl = '') { let appObj; let app = coreState.appByUuid(uuid); if (app && app.appObj) { appObj = app.appObj; } else { if (!opts) { opts = app._options; } let _processInfo; let toShowOnRun = false; appObj = { _configUrl: configUrl, _options: opts, tray: null, uuid: opts.uuid, get identity() { return { uuid: this.uuid, name: this.uuid }; }, _processInfo, toShowOnRun }; _.each(typeof opts.mainWindowOptions === 'object' && opts.mainWindowOptions, (value, key) => { switch (key) { case 'name': break; case 'url': if (isNonEmptyString(opts[key])) { break; } default: opts[key] = value; } }); opts.url = opts.url || 'about:blank'; const isValidUrl = main_1.isValidChromePageUrl(opts.url) || main_1.isHttpUrl(opts.url) || main_1.isFileUrl(opts.url) || main_1.isAboutPageUrl(opts.url) || path.isAbsolute(opts.url); if (!isValidUrl || !main_1.isURLAllowed(opts.url)) { throw new Error(`Invalid URL supplied: ${opts.url}`); } let eOpts = convertOpts.convertToElectron(opts); opts.toShowOnRun = eOpts['autoShow']; eOpts.show = false; appObj.mainWindow = new BrowserWindow(eOpts); appObj.mainWindow.setFrameConnectStrategy(eOpts.frameConnect || 'last'); appObj.id = appObj.mainWindow.id; appObj.mainWindow.webContents.on('did-fail-load', (event, errorCode, errorDescription, validatedUrl, isMainFrame) => { if (isMainFrame) { if (errorCode === -3 || errorCode === 0) { log.writeToLog(1, `ignoring net error ${errorCode} for ${opts.uuid}`, true); } else { log.writeToLog(1, `receiving net error ${errorCode} for ${opts.uuid}`, true); if (!coreState.argo['noerrdialogs'] && configUrl) { const errorMsgForDialog = errorDescription || `error code ${errorCode}`; const errorMessage = opts.loadErrorMessage || `There was an error loading the application: ${errorMsgForDialog}`; dialog.showErrorBox('Fatal Error', errorMessage); } _.defer(() => { Application.close({ uuid: opts.uuid }, true); }); } } }); opts.name = opts.uuid; appObj._options = opts; if (!configUrl) { appObj.launchMode = 'adapter'; } else { appObj.launchMode = undefined; utils_1.sendToRVM({ topic: 'application', action: 'launched-from', sourceUrl: configUrl }).then(response => { appObj.launchMode = response.source; coreState.setAppObj(appObj.id, appObj); }).catch(() => { }); } if (!app) { coreState.addApp(appObj.id, uuid); coreState.setAppOptions(opts, configUrl); } else { coreState.setAppId(uuid, appObj.id); } coreState.setAppObj(appObj.id, appObj); of_events_1.default.emit(route_1.default.application('created', uuid), { topic: 'application', type: 'application-created', uuid }); } return appObj; } function isNonEmptyString(str) { return typeof str === 'string' && str.length > 0; } function checkApplicationAvailability(uuid) { const app = Application.wrap(uuid); if (!app) { return `application with uuid '${uuid}' does not exist`; } else { return null; } } module.exports.Application = Application; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const of_events_1 = require("../of_events"); const route_1 = require("../../common/route"); const ack_1 = require("../api_protocol/transport_strategy/ack"); const api_protocol_base_1 = require("../api_protocol/api_handlers/api_protocol_base"); const core_state_1 = require("../core_state"); const subscription_manager_1 = require("../subscription_manager"); const subscriptionManager = new subscription_manager_1.default(); const channelMap = new Map(); const pendingChannelConnections = new Map(); const CHANNEL_APP_ACTION = 'process-channel-message'; const CHANNEL_ACK_ACTION = 'send-channel-result'; const CHANNEL_CONNECT_ACTION = 'process-channel-connection'; const getChannelId = (identity, channelName) => { const { uuid, name } = identity; return `${uuid}/${name}/${channelName}`; }; const createAckToSender = (identity, messageId, providerIdentity) => { return { action: CHANNEL_ACK_ACTION, payload: { correlationId: messageId, destinationToken: identity, payload: providerIdentity, success: true } }; }; var Channel; (function (Channel) { function addEventListener(targetIdentity, type, listener) { const { uuid, name } = targetIdentity; const eventString = name ? route_1.default.channel(type, uuid, name) : route_1.default.channel(type, uuid); of_events_1.default.on(eventString, listener); return () => { of_events_1.default.removeListener(eventString, listener); }; } Channel.addEventListener = addEventListener; function getAllChannels() { const allChannels = []; channelMap.forEach(channel => { allChannels.push(channel); }); return allChannels; } Channel.getAllChannels = getAllChannels; function getChannelByChannelName(channelName, channelArray) { let providerIdentity; const channels = channelArray || Array.from(channelMap.values()); channels.forEach((channel) => { if (channel.channelName === channelName) { providerIdentity = channel; } }); return providerIdentity; } Channel.getChannelByChannelName = getChannelByChannelName; function createChannel(identity, channelName, allChannels) { if (Channel.getChannelByChannelName(channelName, allChannels)) { const nackString = 'Channel creation failed: Please note that only one channel may be registered per channelName.'; throw new Error(nackString); } const providerApp = core_state_1.getExternalOrOfWindowIdentity(identity); const channelId = getChannelId(identity, channelName); const providerIdentity = Object.assign({}, providerApp, { channelName, channelId }); channelMap.set(channelId, providerIdentity); const { uuid, name } = providerIdentity; const unloadEvent = route_1.default.window('unload', uuid, name, false); const unloadListener = () => subscriptionManager.removeSubscription(identity, channelId); of_events_1.default.once(unloadEvent, unloadListener); const onCloseListener = () => { channelMap.delete(channelId); of_events_1.default.emit(route_1.default.channel('disconnected'), providerIdentity); of_events_1.default.emit(route_1.default.channel('channel-disconnected'), providerIdentity); of_events_1.default.removeListener(unloadEvent, unloadListener); }; subscriptionManager.registerSubscription(onCloseListener, identity, channelId); of_events_1.default.emit(route_1.default.channel('connected'), providerIdentity); of_events_1.default.emit(route_1.default.channel('channel-connected'), providerIdentity); return providerIdentity; } Channel.createChannel = createChannel; function destroyChannel(identity, channelName) { const channel = Channel.getChannelByChannelName(channelName); if (!channel) { const nackString = `Channel ${channelName} does not exist.`; throw new Error(nackString); } else if (channel.uuid !== identity.uuid) { const nackString = 'Channel can only be destroyed from application that created it.'; throw new Error(nackString); } const { channelId } = channel; channelMap.delete(channelId); subscriptionManager.removeSubscription(identity, channelId); } Channel.destroyChannel = destroyChannel; function disconnectFromChannel(identity, channelName) { const disconnectedEvent = 'client-disconnected'; subscriptionManager.removeSubscription(identity, `${disconnectedEvent}-${channelName}`); } Channel.disconnectFromChannel = disconnectFromChannel; function connectToChannel(identity, payload, messageId, ack, nack) { const { channelName, payload: connectionPayload } = payload; const { uuid, name } = identity; const providerIdentity = Channel.getChannelByChannelName(channelName); if (providerIdentity) { const ackToSender = createAckToSender(identity, messageId, providerIdentity); api_protocol_base_1.sendToIdentity(providerIdentity, { action: CHANNEL_CONNECT_ACTION, payload: { ackToSender, providerIdentity, clientIdentity: identity, payload: connectionPayload } }); const disconnectedEvent = 'client-disconnected'; const unloadEvent = route_1.default.window('unload', uuid, name, false); const unloadListener = () => subscriptionManager.removeSubscription(identity, `${disconnectedEvent}-${channelName}`); of_events_1.default.once(unloadEvent, unloadListener); const clientDisconnect = () => { const payload = Object.assign({ channelName }, identity); of_events_1.default.emit(route_1.default.channel(disconnectedEvent), payload); of_events_1.default.removeListener(unloadEvent, unloadListener); }; subscriptionManager.registerSubscription(clientDisconnect, identity, `${disconnectedEvent}-${channelName}`); } else if (identity.runtimeUuid) { ack({ success: true }); } else { const interimNackMessage = 'internal-nack'; nack(interimNackMessage); } } Channel.connectToChannel = connectToChannel; function sendChannelMessage(identity, payload, messageId, ack, nack) { const { uuid, name, payload: messagePayload, action: channelAction, providerIdentity } = payload; const targetIdentity = { uuid, name }; const ackToSender = createAckToSender(identity, messageId, providerIdentity); api_protocol_base_1.sendToIdentity(targetIdentity, { action: CHANNEL_APP_ACTION, payload: { ackToSender, providerIdentity, action: channelAction, senderIdentity: identity, payload: messagePayload } }); } Channel.sendChannelMessage = sendChannelMessage; function sendChannelResult(identity, payload, ack, nack) { const { reason, success, destinationToken, correlationId, payload: ackPayload } = payload; const ackObj = new ack_1.AckMessage(); ackObj.correlationId = correlationId; if (destinationToken) { ackObj.payload = success ? new ack_1.AckPayload(ackPayload) : new ack_1.NackPayload(reason); if (destinationToken.runtimeUuid) { api_protocol_base_1.sendToIdentity({ uuid: destinationToken.runtimeUuid }, ackObj); } else { api_protocol_base_1.sendToIdentity(destinationToken, ackObj); } } else { nack('Ack failed, initial channel destinationToken not found.'); } } Channel.sendChannelResult = sendChannelResult; })(Channel = exports.Channel || (exports.Channel = {})); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const of_events_1 = require("../of_events"); const ProcessTracker = require("../process_tracker.js"); const route_1 = require("../../common/route"); const authenticatedConnections = []; var ExternalApplication; (function (ExternalApplication) { function addEventListener(identity, type, listener) { const evt = route_1.default.externalApplication(type, identity.uuid); of_events_1.default.on(evt, listener); return () => { of_events_1.default.removeListener(evt, listener); }; } ExternalApplication.addEventListener = addEventListener; function removeEventListener(identity, type, listener) { of_events_1.default.removeListener(route_1.default.externalApplication(type, identity.uuid), listener); } ExternalApplication.removeEventListener = removeEventListener; function getInfo(externalApp) { const extProcess = ProcessTracker.getProcessByUuid(externalApp.uuid); return { parent: ((extProcess && extProcess.window && extProcess.window.uuid) ? extProcess.window : null) }; } ExternalApplication.getInfo = getInfo; function addExternalConnection(externalConnObj) { const { uuid } = externalConnObj; authenticatedConnections.push(externalConnObj); of_events_1.default.emit(route_1.default.externalApplication('connected', externalConnObj.uuid), { uuid }); of_events_1.default.emit(route_1.default.externalApplication('connected'), { uuid }); } ExternalApplication.addExternalConnection = addExternalConnection; function getExternalConnectionByUuid(uuid) { return authenticatedConnections.find(c => { return c.uuid === uuid; }); } ExternalApplication.getExternalConnectionByUuid = getExternalConnectionByUuid; function getExternalConnectionById(id) { return authenticatedConnections.find(c => { return c.id === id; }); } ExternalApplication.getExternalConnectionById = getExternalConnectionById; function isRuntimeClient(uuid) { const target = authenticatedConnections.find(c => { return c.uuid === uuid; }); return target ? target.runtimeClient === true : false; } ExternalApplication.isRuntimeClient = isRuntimeClient; function removeExternalConnection(externalConnection) { authenticatedConnections.splice(authenticatedConnections.indexOf(externalConnection), 1); of_events_1.default.emit(route_1.default.externalApplication('disconnected', externalConnection.uuid), { uuid: externalConnection.uuid }); of_events_1.default.emit(route_1.default.externalApplication('disconnected'), { uuid: externalConnection.uuid }); } ExternalApplication.removeExternalConnection = removeExternalConnection; function getAllExternalConnctions() { return authenticatedConnections.slice(0); } ExternalApplication.getAllExternalConnctions = getAllExternalConnctions; function createExternalApplicationOptions(externalOpts) { const externalAppOptions = { id: externalOpts.id, uuid: externalOpts.uuid, runtimeClient: false, nonPersistent: false }; return Object.assign(externalAppOptions, externalOpts); } ExternalApplication.createExternalApplicationOptions = createExternalApplicationOptions; })(ExternalApplication = exports.ExternalApplication || (exports.ExternalApplication = {})); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const path = require("path"); const log_1 = require("../log"); const of_events_1 = require("../of_events"); const route_1 = require("../../common/route"); const coreState = require("../core_state"); const EVENT_TOPIC = 'window'; exports.FILE_DOWNLOAD_EVENTS = { STARTED: 'file-download-started', PROGRESS: 'file-download-progress', COMPLETED: 'file-download-completed' }; exports.downloadLocationMap = new Map(); function hasAccess(identity, fileUuid) { const fileDownload = exports.downloadLocationMap.get(fileUuid); const requestorAnc = coreState.getAppAncestor(identity.uuid); const fileOwnerAnc = fileDownload ? coreState.getAppAncestor(fileDownload.identity.uuid) : void 0; if ((requestorAnc && fileOwnerAnc) && (requestorAnc.uuid === fileOwnerAnc.uuid)) { return true; } return false; } exports.hasAccess = hasAccess; function createWillDownloadEventListener(identity) { return (event, item, webContents) => { const { uuid, name } = identity; try { const fileUuid = electron_1.app.generateGUID(); const getFileEventData = (type, state) => ({ type, state, url: item.getURL(), mimeType: item.getMimeType(), fileName: path.parse(item.getSavePath()).base, originalFileName: item.getFilename(), totalBytes: item.getTotalBytes(), startTime: item.getStartTime(), contentDisposition: item.getContentDisposition(), lastModifiedTime: item.getLastModifiedTime(), eTag: item.getETag(), downloadedBytes: item.getReceivedBytes(), topic: EVENT_TOPIC, uuid, name, fileUuid }); const progressTracker = (event, state) => { try { let reportedState = state; if (state === 'progressing' && item.isPaused()) { reportedState = 'paused'; } of_events_1.default.emit(route_1.default.window(exports.FILE_DOWNLOAD_EVENTS.PROGRESS, uuid, name), getFileEventData(exports.FILE_DOWNLOAD_EVENTS.PROGRESS, state)); } catch (e) { log_1.writeToLog('info', e); } }; of_events_1.default.emit(route_1.default.window(exports.FILE_DOWNLOAD_EVENTS.STARTED, uuid, name), getFileEventData(exports.FILE_DOWNLOAD_EVENTS.STARTED, 'started')); item.on('updated', progressTracker); item.once('done', (event, state) => { try { item.removeAllListeners('updated'); const savePath = item.getSavePath(); exports.downloadLocationMap.set(fileUuid, { fileUuid, path: savePath, identity }); if (state !== 'completed') { log_1.writeToLog('info', `download ${fileUuid} failed, state: ${state}`); } of_events_1.default.emit(route_1.default.window(exports.FILE_DOWNLOAD_EVENTS.COMPLETED, uuid, name), getFileEventData(exports.FILE_DOWNLOAD_EVENTS.COMPLETED, state)); const fileDownloadBrowserWindow = electron_1.BrowserWindow.fromWebContents(webContents); const browserWindowId = fileDownloadBrowserWindow.id; const { url: urlFromDownloadWindow } = coreState.getWindowOptionsById(browserWindowId); if (!urlFromDownloadWindow) { fileDownloadBrowserWindow.close(); } } catch (e) { log_1.writeToLog('info', e); } }); } catch (err) { log_1.writeToLog('info', err); } }; } exports.createWillDownloadEventListener = createWillDownloadEventListener; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const of_events_1 = require("../of_events"); const route_1 = require("../../common/route"); const coreState = require("../core_state"); class FrameInfo { constructor(frameInfo = {}) { this.uuid = ''; this.name = ''; this.parent = { uuid: null, name: null }; this.entityType = 'unknown'; const { uuid, name, parent, entityType } = frameInfo; this.name = name || this.name; this.uuid = uuid || this.uuid; this.parent = parent || this.parent; this.entityType = entityType || this.entityType; } } exports.FrameInfo = FrameInfo; var Frame; (function (Frame) { function addEventListener(targetIdentity, type, listener) { const eventString = route_1.default.frame(type, targetIdentity.uuid, targetIdentity.name); const errRegex = /^Attempting to call a function in a renderer frame that has been closed or released/; let unsubscribe; let browserWinIsDead; const safeListener = (...args) => { try { listener.call(null, ...args); } catch (err) { browserWinIsDead = errRegex.test(err.message); if (browserWinIsDead) { of_events_1.default.removeListener(eventString, safeListener); } } }; of_events_1.default.on(eventString, safeListener); unsubscribe = () => { of_events_1.default.removeListener(eventString, safeListener); }; return unsubscribe; } Frame.addEventListener = addEventListener; function removeEventListener(identity, type, listener) { const browserFrame = coreState.getWindowByUuidName(identity.uuid, identity.name); if (browserFrame) { const id = String(browserFrame.id); of_events_1.default.removeListener(route_1.default.frame(type, id), listener); } } Frame.removeEventListener = removeEventListener; function getInfo(targetIdentity) { const frameInfo = coreState.getInfoByUuidFrame(targetIdentity); if (frameInfo) { return new FrameInfo(frameInfo); } else { return new FrameInfo(targetIdentity); } } Frame.getInfo = getInfo; function getParentWindow(identity) { const app = coreState.getAppByUuid(identity.uuid); const parentWindow = app.children.find((win) => { const ofWin = win.openfinWindow; const frames = ofWin && ofWin.frames; const hasFrame = frames && frames.get(identity.name); return !!hasFrame; }); if (!parentWindow || !parentWindow.openfinWindow) { return new FrameInfo(); } const { uuid, name } = parentWindow.openfinWindow; return getInfo({ uuid, name }); } Frame.getParentWindow = getParentWindow; })(Frame = exports.Frame || (exports.Frame = {})); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const subscription_manager_1 = require("../subscription_manager"); const events_1 = require("events"); const of_events_1 = require("../of_events"); const log = require("../log"); const route_1 = require("../../common/route"); const { globalShortcut } = require('electron'); const subscriptionManager = new subscription_manager_1.default(); const hotkeyOwnershipMap = new Map(); const emitter = new events_1.EventEmitter(); const eventNames = { REGISTERED: 'registered', UNREGISTERED: 'unregistered' }; exports.reservedHotKeys = [ 'CommandOrControl+0', 'CommandOrControl+=', 'CommandOrControl+Plus', 'CommandOrControl+-', 'CommandOrControl+_', 'CommandOrControl+Shift+I', 'F5', 'CommandOrControl+R', 'Shift+F5', 'CommandOrControl+Shift+R' ]; class HotKeyError extends Error { constructor(hotkey, reason) { super(`Failed to register Hotkey: ${hotkey}, ${reason}`); } } exports.HotKeyError = HotKeyError; var GlobalHotkey; (function (GlobalHotkey) { function register(identity, hotkey, listener) { validateRegistration(identity, hotkey); if (emitter.listenerCount(hotkey) > 0) { applyRegistration(identity, hotkey, listener); } else if (!globalShortcut.register(hotkey, constructEmit(hotkey))) { emitter.removeAllListeners(hotkey); throw new HotKeyError(hotkey, 'register call returned undefined'); } else { hotkeyOwnershipMap.set(hotkey, identity.uuid); applyRegistration(identity, hotkey, listener); of_events_1.default.emit(route_1.default.globalHotkey(eventNames.REGISTERED, identity.uuid), { identity, hotkey }); log.writeToLog('info', `${identity.uuid}-${identity.name} registered global hotkey ${hotkey}`); } } GlobalHotkey.register = register; function unregister(identity, hotkey) { emitter.removeAllListeners(hotkey); globalShortcut.unregister(hotkey); hotkeyOwnershipMap.delete(hotkey); of_events_1.default.emit(route_1.default.globalHotkey(eventNames.UNREGISTERED, identity.uuid), { identity, hotkey }); log.writeToLog('info', `${identity.uuid}-${identity.name} unregistered global hotkey ${hotkey}`); } GlobalHotkey.unregister = unregister; function unregisterAll(identity) { const hotkeyOwnedById = []; hotkeyOwnershipMap.forEach((value, key) => { if (value === identity.uuid) { hotkeyOwnedById.push(key); } }); hotkeyOwnedById.forEach((acc) => unregister(identity, acc)); } GlobalHotkey.unregisterAll = unregisterAll; function isRegistered(hotkey) { return globalShortcut.isRegistered(hotkey); } GlobalHotkey.isRegistered = isRegistered; function addEventListener(identity, type, listener) { const evt = route_1.default.globalHotkey(type, identity.uuid); of_events_1.default.on(evt, listener); return () => { of_events_1.default.removeListener(evt, listener); }; } GlobalHotkey.addEventListener = addEventListener; })(GlobalHotkey = exports.GlobalHotkey || (exports.GlobalHotkey = {})); function constructEmit(hotkey) { return () => { emitter.emit(hotkey); }; } function constructUnregister(identity, hotkey, listener) { return () => { emitter.removeListener(hotkey, listener); if (emitter.listenerCount(hotkey) < 1) { GlobalHotkey.unregister(identity, hotkey); } }; } function applyRegistration(identity, hotkey, listener) { emitter.on(hotkey, listener); subscriptionManager.registerSubscription(constructUnregister(identity, hotkey, listener), identity, hotkey); } function validateRegistration(identity, hotkey) { const ownerUuid = hotkeyOwnershipMap.get(hotkey); if (ownerUuid && ownerUuid === identity.uuid) { return; } else if (exports.reservedHotKeys.indexOf(hotkey) > -1) { throw new HotKeyError(hotkey, 'is reserved'); } else { if (globalShortcut.isRegistered(hotkey)) { throw new HotKeyError(hotkey, 'already registered'); } } } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); let util = require('util'); let EventEmitter = require('events').EventEmitter; const subscription_manager_1 = require("../subscription_manager"); const of_events_1 = require("../of_events"); const subscriptionManager = new subscription_manager_1.default(); let callbacks = {}; const NO_SUBS_ERR_STR = 'No subscriptions match'; const ANY_UUID = '*'; const ANY_NAME = '*'; let callbackId = 0; let busEventing, ofBus; function BusEventing() { EventEmitter.call(this); } util.inherits(BusEventing, EventEmitter); busEventing = new BusEventing(); function OFBus() { EventEmitter.call(this); } util.inherits(OFBus, EventEmitter); ofBus = new OFBus(); busEventing.on(of_events_1.default.subscriber.ADDED, subInfo => { const subscriptionInfo = JSON.parse(JSON.stringify(subInfo)); subscriptionInfo.topic = decodeURIComponent(subscriptionInfo.topic); busEventing.emit(generateKey([of_events_1.default.subscriber.ADDED], [subInfo.senderUuid, subInfo.senderName]), subscriptionInfo); }); busEventing.on(of_events_1.default.subscriber.REMOVED, subInfo => { const subscriptionInfo = JSON.parse(JSON.stringify(subInfo)); subscriptionInfo.topic = decodeURIComponent(subscriptionInfo.topic); busEventing.emit(generateKey([of_events_1.default.subscriber.REMOVED], [subInfo.senderUuid, subInfo.senderName]), subscriptionInfo); }); function genCallBackId() { return ++callbackId; } function publish(identity, payload) { const { topic } = payload; const payloadToDeliver = Object.assign({}, payload, { sourceUuid: identity.uuid, sourceWindowName: identity.name, destinationUuid: ANY_UUID }); dispatchToSubscriptions(topic, identity, null, null, payloadToDeliver, true); } function send(identity, payload) { let { topic, destinationUuid, destinationWindowName } = payload; let payloadToDeliver = Object.assign({ sourceUuid: identity.uuid, sourceWindowName: identity.name, }, payload); if (!dispatchToSubscriptions(topic, identity, destinationUuid, destinationWindowName, payloadToDeliver)) { throw new Error(NO_SUBS_ERR_STR); } } function dispatchToSubscriptions(topic, identity, destUuid, destName, payload, sendToAll) { const keys = generateSendKeys(topic, identity, { uuid: destUuid || ANY_UUID, name: destName || ANY_NAME }); if (sendToAll) { return ofBus.emit(keys.fromAny, payload) + ofBus.emit(keys.fromApp, payload) + ofBus.emit(keys.fromWin, payload); } return ofBus.emit(keys.fromAny, payload) || ofBus.emit(keys.fromApp, payload) || ofBus.emit(keys.fromWin, payload); } function emitSubscriberAdded(identity, payload) { const senderUuid = payload.sourceUuid || ANY_UUID; const eventingPayload = { senderUuid: senderUuid, senderName: senderUuid, uuid: identity.uuid, name: identity.name, topic: payload.topic, directMsg: payload.sourceWindowName !== ANY_NAME ? payload.sourceWindowName : false }; busEventing.emit(of_events_1.default.subscriber.ADDED, eventingPayload); } function emitSubscriberRemoved(identity, payload) { const senderUuid = payload.sourceUuid || ANY_UUID; const eventingPayload = { senderUuid: senderUuid, senderName: senderUuid, uuid: identity.uuid, name: identity.name, topic: payload.topic, directMsg: payload.sourceWindowName !== ANY_NAME ? payload.sourceWindowName : false }; busEventing.emit(of_events_1.default.subscriber.REMOVED, eventingPayload); } function subscribe(identity, payload, listener) { let topic = payload.topic; let senderUuid = payload.sourceUuid || ANY_UUID; let senderName = payload.sourceWindowName || ANY_NAME; let cbId = genCallBackId(); callbacks['' + cbId] = listener; let keys = generateSubscribeKeys(topic, { uuid: senderUuid, name: senderName }, identity); ofBus.on(keys.toAny, listener); ofBus.on(keys.toWin, listener); ofBus.on(keys.toApp, listener); var unsubItem = { cbId, unsubscribe: () => { ofBus.removeListener(keys.toAny, listener); ofBus.removeListener(keys.toWin, listener); ofBus.removeListener(keys.toApp, listener); } }; return unsubItem; } function subscriberAdded(identity, listener) { let { uuid, name } = identity; let cbId = genCallBackId(); let listenerStrs = generateListenerKeys(of_events_1.default.subscriber.ADDED, uuid, name); let subMgrStr = listenerStrs[0]; let unsubItem; callbacks['' + cbId] = listener; listenerStrs.forEach(listenerStr => { busEventing.on(listenerStr, listener); }); unsubItem = { cbId, unsubscribe: () => { removeSubscriberAdded(identity, cbId); } }; subscriptionManager.registerSubscription(unsubItem.unsubscribe, identity, subMgrStr); return unsubItem; } function removeSubscriberAdded(identity, cbId) { let { uuid, name } = identity; let callback = callbacks['' + cbId]; if (!callback) { return; } let listenerStrs = generateListenerKeys(of_events_1.default.subscriber.ADDED, uuid, name); listenerStrs.forEach(listenerStr => { busEventing.removeListener(listenerStr, callback); }); delete callbacks['' + cbId]; } function subscriberRemoved(identity, listener) { let cbId = genCallBackId(); let { uuid, name } = identity; let listenerStrs = generateListenerKeys(of_events_1.default.subscriber.REMOVED, uuid, name); let subMgrStr = listenerStrs[0]; let unsubItem; listenerStrs.forEach(listenerStr => { busEventing.on(listenerStr, listener); }); callbacks['' + cbId] = listener; unsubItem = { cbId, unsubscribe: () => { removeSubscriberRemoved(identity, cbId); } }; subscriptionManager.registerSubscription(unsubItem.unsubscribe, identity, subMgrStr); return unsubItem; } function removeSubscriberRemoved(identity, cbId) { let callback = callbacks['' + cbId]; let { uuid, name } = identity; if (!callback) { return; } let listenerStrs = generateListenerKeys(of_events_1.default.subscriber.REMOVED, uuid, name); listenerStrs.forEach(listenerStr => { busEventing.removeListener(listenerStr, callback); }); delete callbacks['' + cbId]; } function generateKey(...args) { return args.map(arg => encodeKeyPart(...arg)).join(':'); } function encodeKeyPart(...args) { return args.map(arg => encodeURIComponent(arg)).join('/'); } function generateSendKeys(topic, source, dest) { return { fromWin: generateKey([topic], [source.uuid, source.name], [dest.uuid, dest.name]), fromApp: generateKey([topic], [source.uuid, ANY_NAME], [dest.uuid, dest.name]), fromAny: generateKey([topic], [ANY_UUID, ANY_NAME], [dest.uuid, dest.name]) }; } function generateSubscribeKeys(topic, source, dest) { return { toWin: generateKey([topic], [source.uuid, source.name], [dest.uuid, dest.name]), toApp: generateKey([topic], [source.uuid, source.name], [dest.uuid, ANY_NAME]), toAny: generateKey([topic], [source.uuid, source.name], [ANY_UUID, ANY_NAME]) }; } function generateListenerKeys(topic, uuid, name) { return [ generateKey([topic], [uuid, name]), generateKey([topic], [uuid, ANY_NAME]), generateKey([topic], [ANY_UUID, ANY_NAME]) ]; } function raiseSubscriberEvent(eventName, evtObj) { busEventing.emit(eventName, evtObj); } module.exports.InterApplicationBus = { publish, send, subscribe, emitSubscriberAdded, emitSubscriberRemoved, subscriberAdded, removeSubscriberAdded, subscriberRemoved, removeSubscriberRemoved, raiseSubscriberEvent }; "use strict"; function Notification() { this.getCurrentNotification = function () {}; this.getCurrent = function () {}; } Notification.prototype.close = function (callback) { if (callback) { callback(); } }; Notification.prototype.sendMessage = function () /*message, callback*/{}; Notification.prototype.sendMessageToApplication = function () /*message, callback*/{}; module.exports.Notification = Notification; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var NoteAction; (function (NoteAction) { NoteAction[NoteAction["dummmy"] = 0] = "dummmy"; NoteAction[NoteAction["noteMessage"] = 1] = "noteMessage"; NoteAction[NoteAction["create_external"] = 2] = "create_external"; NoteAction[NoteAction["proxied_create_call"] = 3] = "proxied_create_call"; NoteAction[NoteAction["created_notes"] = 4] = "created_notes"; NoteAction[NoteAction["request_note_close"] = 5] = "request_note_close"; NoteAction[NoteAction["drag"] = 6] = "drag"; NoteAction[NoteAction["mouseover"] = 7] = "mouseover"; NoteAction[NoteAction["mouseenter"] = 8] = "mouseenter"; NoteAction[NoteAction["mouseleave"] = 9] = "mouseleave"; NoteAction[NoteAction["mouseout"] = 10] = "mouseout"; NoteAction[NoteAction["mousemove"] = 11] = "mousemove"; NoteAction[NoteAction["create"] = 12] = "create"; NoteAction[NoteAction["animating"] = 13] = "animating"; NoteAction[NoteAction["message_from_note"] = 14] = "message_from_note"; NoteAction[NoteAction["qQuery"] = 15] = "qQuery"; NoteAction[NoteAction["qQueryResponse"] = 16] = "qQueryResponse"; NoteAction[NoteAction["qQueryUpdate"] = 17] = "qQueryUpdate"; NoteAction[NoteAction["click"] = 18] = "click"; NoteAction[NoteAction["close"] = 19] = "close"; NoteAction[NoteAction["dismiss"] = 20] = "dismiss"; NoteAction[NoteAction["error"] = 21] = "error"; NoteAction[NoteAction["message"] = 22] = "message"; NoteAction[NoteAction["show"] = 23] = "show"; })(NoteAction || (NoteAction = {})); exports.default = NoteAction; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Rx = require("rx"); exports.createdNotes = new Rx.Subject(); exports.creates = new Rx.Subject(); exports.pending = new Rx.Subject(); exports.position = new Rx.Subject(); exports.removes = new Rx.Subject(); exports.requestNoteClose = new Rx.Subject(); exports.isAnimating = new Rx.BehaviorSubject(false); exports.removesCounter = exports.removes.map(() => -1); exports.createsCounter = exports.creates.map(() => 1); exports.runningTotal = Rx.Observable.merge(exports.removesCounter, exports.createsCounter) .scan((acc, value) => acc + value); exports.noteStack = Rx.Observable.merge(exports.createdNotes.map((x) => { return { create: 1, name: x.identity.name, uuid: x.identity.uuid, opacity: x.options.finalOpacity }; }), exports.removes.map((x) => { return { create: 0, name: x.name, uuid: x.uuid, opacity: null }; })) .distinctUntilChanged() .scan((acc, value) => { if (value.create) { const idxRemoved = acc.map(x => x.name).indexOf(value.name); if (idxRemoved === -1) { acc.push({ name: value.name, uuid: value.uuid, opacity: value.opacity }); } } else { const idxRemoved = acc.map(x => x.name).indexOf(value.name); if (idxRemoved !== -1) { acc.splice(idxRemoved, 1); } } return acc; }, []); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const main_1 = require("../../../common/main"); class NoteConfig { constructor(opts) { this.windowOpts = { alwaysOnTop: true, autoShow: true, contextMenu: true, cornerRounding: { height: 6, width: 6 }, defaultHeight: 80, defaultWidth: 300, draggable: false, frame: false, isNotification: true, maxHeight: 80, maxWidth: 300, message: '', name: 'newNotifications' + Math.random(), opacity: 0, resizable: false, resize: false, saveWindowState: false, showTaskbarIcon: false, state: 'normal', url: '' }; if (!opts.url) { throw new Error('Notifications require a url'); } this.windowOpts.url = opts.url; this.windowOpts.message = opts.message || ''; this.windowOpts.timeout = opts.timeout || 5000; this.onClick = opts.onClick || main_1.noop; this.onClose = opts.onClose || main_1.noop; this.onDismiss = opts.onDismiss || main_1.noop; this.onError = opts.onError || main_1.noop; this.onMessage = opts.onMessage || main_1.noop; this.onShow = opts.onShow || main_1.noop; } } exports.NoteConfig = NoteConfig; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Rx = require("rx"); const seqs = require("./observable_sequences"); const note_action_1 = require("./note_action"); const { System } = require('../system'); const { Window } = require('../window'); const { Application } = require('../application'); const { sendToIdentity } = require('../../api_protocol/api_handlers/api_protocol_base'); const of_events_1 = require("../../of_events"); const { writeToLog } = require('../../log'); const _ = require('underscore'); const route_1 = require("../../../common/route"); const NOTE_APP_UUID = 'service:notifications'; const MAX_NOTES = 5; const positionWindows = _.debounce(positionWindowsImmediate, 300, false); const idToNameMap = {}; const noteNameToParent = {}; const nameToNoteId = {}; const pendingExternalNoteRequests = []; const qCounterTarget = { name: '', uuid: '' }; const NOTE_WIDTH = 300; const NOTE_PAD_RIGHT = 10; const NOTE_WIDTH_AND_PAD = NOTE_WIDTH + NOTE_PAD_RIGHT; const POSITION_ANIMATION_DURATION = 400; const NOTE_HEIGHT = 90; const NOTE_TOP_MARGIN = 70; let askedFor = 0; let created = 0; let pendindNotes = []; const potentialQCounterTargets = []; const notesToBeCreated = []; let proxyAppInitialized = false; let proxyAppReady = false; of_events_1.default.once('notification-service-ready', () => { try { pendingExternalNoteRequests.forEach(noteFn => noteFn()); proxyAppReady = true; } catch (e) { writeToLog('info', e); } }); of_events_1.default.on(route_1.default.window('closed', '*'), (e) => { setTimeout(() => { cleanPendingNotes(); try { const { uuid, name } = e.data[0]; if (Window.isNotification(name)) { seqs.removes.onNext({ uuid, name }); } } catch (e) { writeToLog('info', e); } }, 10); }); of_events_1.default.on(route_1.default.application('window-end-load', '*'), (e) => { try { const { uuid, name } = e.data[0]; const notesToClose = getCurrNotes().filter((note) => { let parentWasRefreshed = false; Object.keys(noteNameToParent).forEach((nameKey) => { const { name: nameFromHash } = noteNameToParent[nameKey]; if (nameFromHash === name) { parentWasRefreshed = true; } }); return parentWasRefreshed; }); notesToClose.forEach((id) => { Window.close(id); }); cleanPendingNotes({ uuid, name }); if (qCounterTarget.uuid === uuid && qCounterTarget.name === name) { try { Window.close({ uuid, name: Window.QUEUE_COUNTER_NAME }); } catch (e) { writeToLog('info', e); } } } catch (e) { writeToLog('info', e); } }); seqs.requestNoteClose .subscribe((req) => { try { const noteIsOpen = windowIsValid(req.id); if (noteIsOpen) { const ns = getCurrNotes(); const mousePos = System.getMousePosition(); const monitorInfo = getPrimaryMonitorAvailableRect(); const mouseOver = mouseisOverNotes(mousePos, monitorInfo, ns.length); if (!mouseOver || req.data.force) { closeNotification(req); } else { scheduleNoteClose(req, 1000); } } else { removePendingNote(req.id); } } catch (e) { writeToLog('info', e); } }); seqs.position .subscribe(liveNotes => { positionWindows(liveNotes); }); seqs.noteStack .subscribe(liveNotes => { seqs.position.onNext(liveNotes); }); seqs.isAnimating .subscribe((animationPayload) => { const payload = { action: note_action_1.default.animating, data: animationPayload, id: {} }; of_events_1.default.emit(route_1.default('notifications', 'listener/'), payload); }); seqs.removes.subscribe((removedOpts) => { cleanPendingNotes(); try { if (shouldCreatePendingNote()) { createPendingNote(); } assignAndUpdateQCounter(); } catch (e) { writeToLog('info', e); } }); function noteStackCount() { let pendingNotYetCounted = 0; const numNotes = System.getAllWindows() .reduce((prev, currApp) => { const { childWindows } = currApp; const childNoteWindows = childWindows.filter((win) => { const { name } = win; if (notesToBeCreated.indexOf(name) !== -1) { --pendingNotYetCounted; } return Window.isNotification(name); }); return prev.concat(childNoteWindows); }, []).length; const nsCount = numNotes + (askedFor - created) + pendingNotYetCounted; return Math.max(nsCount, 0); } function getCurrNotes() { return System.getAllWindows() .reduce((prev, currApp) => { const { childWindows } = currApp; const childrenAsIdentities = childWindowsAsIdentities(childWindows, currApp.uuid); return prev.concat(childrenAsIdentities); }, []); } function childWindowsAsIdentities(childWindows, appUuid) { return childWindows .filter((win) => Window.isNotification(win.name)) .map((win) => { return { name: win.name, uuid: appUuid }; }); } function inPotentialQCounterTargets(id) { const { uuid, name } = id; let found = false; const len = potentialQCounterTargets.length; for (let i = 0; i < len; i++) { const target = potentialQCounterTargets[i]; const nameMatch = target.name === name; const uuidMatch = target.uuid === uuid; const match = nameMatch && uuidMatch; if (match) { found = true; break; } } return found; } function qCounterTargetIsValid() { const { uuid, name } = qCounterTarget; return uuid && name && windowIsValid(qCounterTarget); } function requestNoteCreation(noteData, parent) { const { options: { name } } = noteData; const noteStackLen = noteStackCount(); if (noteStackLen >= MAX_NOTES) { pendindNotes.push({ noteData, parent }); } else { const { ack } = noteData; try { invokeCreateAck(ack); ++askedFor; notesToBeCreated.push(name); } catch (error) { writeToLog('error', error); } } updateQcounterCount(parent); } function updateQcounterCount(identity) { const { name, uuid } = identity; if (!inPotentialQCounterTargets(identity)) { potentialQCounterTargets.push(identity); } if (!qCounterTargetIsValid()) { qCounterTarget.uuid = uuid; qCounterTarget.name = name; } assignAndUpdateQCounter(); } function assignQCounterToWindow() { const numTargets = potentialQCounterTargets.length; for (let i = 0; i < numTargets; i++) { const id = potentialQCounterTargets[i]; if (windowIsValid(id)) { qCounterTarget.uuid = id.uuid; qCounterTarget.name = id.name; break; } } } function assignAndUpdateQCounter() { let payload; let sendString; if (!qCounterTargetIsValid()) { assignQCounterToWindow(); } payload = createQCounterNumPendingMessage(); sendString = noteTopicStr(qCounterTarget.uuid, qCounterTarget.name, false); of_events_1.default.emit(sendString, payload); } function createQCounterNumPendingMessage() { return { action: note_action_1.default.qQueryUpdate, data: { numPending: pendindNotes.length }, id: {} }; } function positionWindowsImmediate(liveNotes) { try { const { bottom } = getPrimaryMonitorAvailableRect(); const defaultTop = bottom - 100; let numNotes; let animationFunction; updateAnimationState(true); liveNotes = liveNotes.filter(opts => { return windowIsValid(opts); }); numNotes = liveNotes.length; animationFunction = genAnimationFunction(defaultTop, numNotes); liveNotes.forEach(animationFunction); } catch (error) { writeToLog('error', error); } } function genAnimationFunction(defaultTop, numNotes) { return (noteWin, idx) => { const { name, uuid } = noteWin; const identity = { name, uuid }; const opacity = isNaN(noteWin.opacity) ? 1 : noteWin.opacity; const animationTransitions = { opacity: { duration: 1000, opacity }, position: { duration: POSITION_ANIMATION_DURATION, top: (defaultTop - (numNotes - idx) * NOTE_HEIGHT) + NOTE_TOP_MARGIN } }; const animationCallback = () => { if (idx === numNotes - 1) { updateAnimationState(false); } }; const animationOptions = {}; Window.animate(identity, animationTransitions, animationOptions, animationCallback); }; } function mouseisOverNotes(mousePos, monitorInfo, noteStackLength) { return mousePos.left > monitorInfo.right - 310 && monitorInfo.bottom - mousePos.top < noteStackLength * 110; } function genBaseAnimateOpts() { return { opacity: { duration: 300, opacity: 0 }, position: { duration: 300 } }; } function createNoteProxyApp() { Application.create({ autoShow: false, name: NOTE_APP_UUID, nonPersistent: true, url: 'about:blank', uuid: NOTE_APP_UUID }); Application.run({ name: NOTE_APP_UUID, uuid: NOTE_APP_UUID }); proxyAppInitialized = true; } function emitCreateMsg(msg, proxyUuid) { const { data } = msg; data.uuid = NOTE_APP_UUID; data.name = NOTE_APP_UUID; data.uuidOfProxiedApp = proxyUuid; msg.action = note_action_1.default.proxied_create_call; of_events_1.default.emit(noteTopicStr(NOTE_APP_UUID, NOTE_APP_UUID, true), msg); } function createNoteViaProxy(msg) { const { id: { uuid } } = msg; if (!proxyAppInitialized) { createNoteProxyApp(); } if (!proxyAppReady) { pendingExternalNoteRequests.push(emitCreateMsg.bind(null, msg, uuid)); } else { emitCreateMsg(msg, uuid); } } function handleNoteCreate(msg) { const { data, id: { uuid, name } } = msg; let isOwnedByProxy = false; let parent; const { options } = data; const notificationId = options.notificationId; const uuidOfProxiedApp = options.uuidOfProxiedApp; const noteName = options.name; if (notificationId !== undefined && uuidOfProxiedApp !== undefined) { isOwnedByProxy = true; if (!idToNameMap[uuidOfProxiedApp]) { idToNameMap[uuidOfProxiedApp] = {}; } idToNameMap[uuidOfProxiedApp][notificationId] = noteName; nameToNoteId[noteName] = notificationId; parent = { isOwnedByProxy, name: uuidOfProxiedApp, uuid: uuidOfProxiedApp }; } else { parent = { uuid, name, isOwnedByProxy }; } noteNameToParent[noteName] = parent; requestNoteCreation(data, parent); } function handleNoteCreated(msg) { const { data: { options } } = msg; const { uuid, name } = options; const identity = { uuid, name }; ++created; const idx = notesToBeCreated.indexOf(options.name); if (idx !== -1) { notesToBeCreated.splice(idx, 1); } seqs.createdNotes.onNext({ identity, options }); } function routeRequest(id, msg, ack) { const { action, data } = msg; data.ack = ack; switch (action) { case note_action_1.default.create_external: createNoteViaProxy(msg); break; case note_action_1.default.create: handleNoteCreate(msg); break; case note_action_1.default.created_notes: handleNoteCreated(msg); break; case note_action_1.default.close: requestNoteClose(msg); break; case note_action_1.default.click: dispatchEvent('click', msg); break; case note_action_1.default.dismiss: requestNoteClose(msg); break; case note_action_1.default.show: dispatchEvent('show', msg); break; case note_action_1.default.error: dispatchEvent('error', msg); break; case note_action_1.default.message: dispatchMessageToNote(msg); break; case note_action_1.default.message_from_note: dispatchEvent('message', msg); break; case note_action_1.default.animating: seqs.isAnimating.onNext(data); break; case note_action_1.default.qQuery: ack({ success: true, data: pendindNotes.length }); break; default: break; } } exports.routeRequest = routeRequest; function requestNoteClose(msg) { const { data, id: { uuid } } = msg; if (data.notificationId !== undefined) { const destName = idToNameMap[uuid][data.notificationId]; msg.id.name = destName; msg.id.uuid = NOTE_APP_UUID; } seqs.requestNoteClose.onNext(msg); } function dispatchMessageToNote(msg) { const { data, id: { uuid, name } } = msg; try { if (data.notificationId !== undefined) { const destName = idToNameMap[uuid][data.notificationId]; of_events_1.default.emit(noteTopicStr(NOTE_APP_UUID, destName), msg); } else { of_events_1.default.emit(noteTopicStr(uuid, name), msg); } } catch (e) { writeToLog('info', e); } } function dispatchEvent(event, msg) { const { id: { uuid, name } } = msg; const { isOwnedByProxy } = noteNameToParent[name]; if (isOwnedByProxy) { sendEventToExternal(name, event); } else { of_events_1.default.emit(noteTopicStr(uuid, noteNameToParent[name].name), msg); } } function sendEventToExternal(name, eventType) { const { uuid: proxiedAppUuid } = noteNameToParent[name]; sendToIdentity({ name: proxiedAppUuid, uuid: proxiedAppUuid }, { action: 'process-notification-event', payload: { payload: { notificationId: nameToNoteId[name] }, type: eventType } }); } function windowIsValid(identity) { let isValid; try { const openfinWindow = Window.wrap(identity.uuid, identity.name); const browserWindow = openfinWindow && openfinWindow.browserWindow; if (!browserWindow) { isValid = false; } else if (browserWindow.isDestroyed()) { isValid = false; } else { isValid = true; } } catch (e) { isValid = false; } return isValid; } function cleanPendingNotes(winToExclude = false) { const notesWithValidParents = []; pendindNotes.forEach((note) => { const { parent: { uuid, name } } = note.noteData.options; const parentExist = windowIsValid({ uuid, name }); const excludeUuid = winToExclude && winToExclude.uuid === note.parent.uuid; const excludeName = winToExclude && winToExclude.name === note.parent.name; const excludeNote = excludeUuid && excludeName; if (parentExist && !excludeNote) { notesWithValidParents.push(note); } }); pendindNotes = notesWithValidParents; } function noteTopicStr(uuid, name, isGeneral) { return isGeneral ? route_1.default('notifications', 'listener/') : route_1.default('notifications', 'listener', uuid, name, true); } function closeNotification(req) { const animateOpts = genBaseAnimateOpts(); const { id } = req; if (req.data.swipe) { animateOpts.position.relative = true; animateOpts.position.left = 300; } updateAnimationState(true); Window.animate(id, animateOpts, {}, () => { Window.close(id); dispatchEvent('close', req); }, (e) => { writeToLog('info', e); }); } function updateAnimationState(animationState) { seqs.isAnimating.onNext({ animating: animationState, from: {} }); } function getPrimaryMonitorAvailableRect() { const { primaryMonitor: { availableRect } } = System.getMonitorInfo(); return availableRect; } function getPrimaryMonitorRect() { const { primaryMonitor: { monitorRect } } = System.getMonitorInfo(); return monitorRect; } function scheduleNoteClose(req, timeout) { Rx.Scheduler.default.scheduleFuture(req, timeout, (scheduler, request) => { seqs.requestNoteClose.onNext(request); return scheduler; }); } function shouldCreatePendingNote() { const lessThanMax = noteStackCount() <= MAX_NOTES; const pendingNotesExist = pendindNotes.length > 0; return lessThanMax && pendingNotesExist; } function createPendingNote() { const nextNote = pendindNotes[0].noteData; const noteHasValidParent = windowIsValid({ name: nextNote.options.uuid, uuid: nextNote.options.uuid }); if (noteHasValidParent) { const { ack } = nextNote; try { invokeCreateAck(ack); ++askedFor; pendindNotes.shift(); } catch (error) { writeToLog('error', error); } } } function removePendingNote(identity) { pendindNotes = pendindNotes.filter(pendingNote => { return pendingNote.noteData.options.name !== identity.name; }); assignAndUpdateQCounter(); } function invokeCreateAck(ack) { const { bottom, right } = getPrimaryMonitorRect(); ack({ data: { left: right - NOTE_WIDTH_AND_PAD, top: bottom }, success: true }); } function addEventListener(identity, type, payload, listener) { const { uuid, name } = identity; const isGeneral = type === 'general'; const eventName = noteTopicStr(uuid, name, isGeneral); of_events_1.default.on(eventName, listener); return () => of_events_1.default.removeListener(eventName, listener); } exports.addEventListener = addEventListener; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const fs = require('fs'); const os = require('os'); const electron = require('electron'); const electronApp = electron.app; const electronBrowserWindow = electron.BrowserWindow; const session = electron.session; const shell = electron.shell; const { crashReporter, idleState } = electron; const path = require('path'); const crypto = require('crypto'); const _ = require('underscore'); const convertOptions = require('../convert_options.js'); const coreState = require('../core_state.js'); const external_application_1 = require("./external_application"); const log = require('../log.js'); const of_events_1 = require("../of_events"); const ProcessTracker = require('../process_tracker.js'); const socketServer = require('../transports/socket_server').server; const portDiscovery = require('../port_discovery').portDiscovery; const route_1 = require("../../common/route"); const preload_scripts_1 = require("../preload_scripts"); const cached_resource_fetcher_1 = require("../cached_resource_fetcher"); const chromium_socket_1 = require("../transports/chromium_socket"); const cached_resource_fetcher_2 = require("../cached_resource_fetcher"); const defaultProc = { getCpuUsage: function () { return 0; }, getNonPagedPoolUsage: function () { return 0; }, getPagedPoolUsage: function () { return 0; }, getPageFaultCount: function () { return 0; }, getPagefileUsage: function () { return 0; }, getPeakNonPagedPoolUsage: function () { return 0; }, getPeakPagedPoolUsage: function () { return 0; }, getPeakPagefileUsage: function () { return 0; }, getPeakWorkingSetSize: function () { return 0; }, getWorkingSetSize: function () { return 0; } }; let MonitorInfo; let Session; let rvmBus; let defaultSession; electronApp.on('ready', function () { MonitorInfo = require('../monitor_info.js'); Session = require('../session').default; rvmBus = require('../rvm/rvm_message_bus').rvmMessageBus; MonitorInfo.on('monitor-info-changed', payload => { of_events_1.default.emit(route_1.default.system('monitor-info-changed'), payload); }); Session.on('session-changed', payload => { of_events_1.default.emit(route_1.default.system('session-changed'), payload); }); Session.on('idle-state-changed', payload => { of_events_1.default.emit(route_1.default.system('idle-state-changed'), payload); }); defaultSession = session.defaultSession; }); electronApp.on('synth-desktop-icon-clicked', payload => { payload.topic = 'system'; payload.type = 'desktop-icon-clicked'; of_events_1.default.emit(route_1.default.system('desktop-icon-clicked'), payload); }); const eventPropagationMap = new Map(); eventPropagationMap.set(route_1.default.externalApplication('connected'), 'external-application-connected'); eventPropagationMap.set(route_1.default.externalApplication('disconnected'), 'external-application-disconnected'); eventPropagationMap.forEach((systemEvent, eventString) => { of_events_1.default.on(eventString, payload => { const systemEventProps = { topic: 'system', type: systemEvent }; const initialPayload = Array.isArray(payload.data) ? payload.data : [payload]; of_events_1.default.emit(route_1.default.system(systemEvent), Object.assign({}, ...initialPayload, systemEventProps)); }); }); exports.System = { addEventListener: function (type, listener) { of_events_1.default.on(route_1.default.system(type), listener); var unsubscribe = () => { of_events_1.default.removeListener(route_1.default.system(type), listener); }; return unsubscribe; }, authenticateResourceFetch: function (identity, options) { cached_resource_fetcher_2.authenticateFetch(options.uuid, options.username, options.password); }, clearCache: function (identity, options, resolve) { var settings = options || {}; const availableStorages = ['appcache', 'cookies', 'filesystem', 'indexdb', 'localstorage', 'shadercache', 'websql', 'serviceworkers']; const storages = []; if (typeof settings.localStorage === 'boolean') { settings.localstorage = settings.localStorage; } if (Object.keys(settings).length === 0) { settings.cache = true; } if (typeof settings.cache === 'boolean') { settings.filesystem = settings.filesystem || settings.cache; settings.indexdb = settings.indexdb || settings.cache; settings.shadercache = settings.shadercache || settings.cache; settings.websql = settings.websql || settings.cache; settings.serviceworkers = settings.serviceworkers || settings.cache; } availableStorages.forEach(function (key) { if (settings[key]) { storages.push(key); } }); const cacheOptions = { storages: storages, quotas: ['temporary', 'persistent', 'syncable'] }; electronApp.vlog(1, `clearCache ${JSON.stringify(storages)}`); cached_resource_fetcher_2.clearCacheInvoked(true); defaultSession.clearCache(() => { defaultSession.clearStorageData(cacheOptions, () => { resolve(); }); }); }, createProxySocket: function (options, callback, errorCallback) { chromium_socket_1.createChromiumSocket(Object.assign({}, options, { callback, errorCallback })); }, authenticateProxySocket: function (options) { const url = options && options.url; electronApp.vlog(1, `authenticateProxySocket ${url}`); chromium_socket_1.authenticateChromiumSocket(options); }, deleteCacheOnExit: function (callback, errorCallback) { const folders = [{ name: electronApp.getPath('userData') }, { name: electronApp.getPath('userDataRoot'), deleteIfEmpty: true }]; const publishSuccess = rvmBus.publish({ topic: 'cleanup', folders }); if (publishSuccess) { callback(); } else { errorCallback('Failed to send a message to the RVM.'); } }, exit: function () { electronApp.quit(); }, getAllWindows: function () { return coreState.getAllWindows(); }, getAllApplications: function () { return coreState.getAllApplications(); }, getAppAssetInfo: function (identity, options, callback, errorCallback) { options.srcUrl = coreState.getConfigUrlByUuid(identity.uuid); var appAssetsFetcher = require('../rvm/runtime_initiated_topics/app_assets').appAssetsFetcher; appAssetsFetcher.fetchAppAsset(options.srcUrl, options.alias, callback, errorCallback); }, getCommandLineArguments: function () { return electronApp.getCommandLineArguments(); }, getConfig: function () { return coreState.getStartManifest(); }, getCrashReporterState: function () { return crashReporter.crashReporterState(); }, getDeviceUserId: function () { const hash = crypto.createHash('sha256'); let hostToken; let username; if (process.platform === 'darwin') { hostToken = os.networkInterfaces().en0[0].mac; username = process.env.USER; } else { hostToken = electronApp.getHostToken(); username = process.env.USERNAME; } if (!username || !hostToken) { throw new Error(`One of username (${username}) or host token (${hostToken}) not defined`); } hash.update(hostToken); hash.update(username); return hash.digest('hex'); }, getDeviceId: function () { if (process.platform === 'win32') { return electronApp.getHostToken(); } else { const hash = crypto.createHash('sha256'); const macAddress = os.networkInterfaces().en0[0].mac; if (!macAddress) { throw new Error(`MAC address (${macAddress}) not defined`); } hash.update(macAddress); return hash.digest('hex'); } }, getEntityInfo: function (identity) { return coreState.getEntityInfo(identity); }, getEnvironmentVariable: function (varsToExpand) { if (Array.isArray(varsToExpand)) { return varsToExpand.reduce(function (result, envVar) { result[envVar] = process.env[envVar] || null; return result; }, {}); } else { return process.env[varsToExpand] || null; } }, getFocusedWindow: function () { const { id } = electronBrowserWindow.getFocusedWindow() || {}; const { uuid, name } = coreState.getWinObjById(id) || {}; return uuid ? { uuid, name } : null; }, getHostSpecs: function () { let state = new idleState(); const theme = (process.platform === 'win32') ? { aeroGlassEnabled: electronApp.isAeroGlassEnabled() } : {}; return Object.assign({ cpus: os.cpus(), memory: os.totalmem(), name: electronApp.getSystemName(), arch: electronApp.getSystemArch(), gpu: { name: electronApp.getGpuName() }, screenSaver: state.isScreenSaverRunning(), }, theme); }, getLog: function (name, resolve) { var pathSafeName = path.basename(name); if (pathSafeName === name) { var pattern = /^debug.*\.log$/; if (pattern.test(pathSafeName)) { fs.readFile(electronApp.getPath('userCache') + '/' + pathSafeName, { encoding: 'utf8' }, (err, data) => { if (!err) { resolve(undefined, data); } else { resolve(`Could not read log file ${name}`); } }); } else { resolve(`${name} is not a valid log file.`); } } else { resolve('Only log file in the base cache directory are supported.'); } }, getLogList: function (callback, options) { fs.readdir(electronApp.getPath('userCache'), function (err, files) { let opts = options || {}; if (!err) { let pattern = opts.pattern || /^debug+(\w)*\.log$/; var logFiles = _.filter(files, fileName => { return pattern.test(fileName); }); var fileStats = []; if (logFiles.length) { var index = 0; var processFileStats = function () { var name = logFiles[index++]; fs.stat(electronApp.getPath('userCache') + '/' + name, (err, stats) => { if (!err) { fileStats.push({ name: name, size: stats.size, date: stats.mtime }); } if (index < logFiles.length) { processFileStats(); } else { if (typeof callback === 'function') { callback(undefined, fileStats); } } }); }; processFileStats(); } else if (typeof callback === 'function') { callback(undefined, fileStats); } } else if (typeof callback === 'function') { callback('Could not locate any log files'); } }); }, getMachineId: function () { if (process.platform === 'win32') { const registryInfo = this.readRegistryValue('HKEY_LOCAL_MACHINE', 'SOFTWARE\\Microsoft\\Cryptography', 'MachineGuid'); return registryInfo.data; } else if (process.platform === 'darwin') { return electronApp.getMachineId(); } else { return ''; } }, getMinLogLevel: function () { try { const logLevel = electronApp.getMinLogLevel(); return log.logLevelMappings.get(logLevel); } catch (e) { return e; } }, getMonitorInfo: function () { return MonitorInfo.getInfo('api-query'); }, getMousePosition: function () { return MonitorInfo.getMousePosition(); }, getProcessList: function () { let allApps = coreState.getAllApplications(); let runningAps = allApps.filter(app => { return app.isRunning; }); let processList = runningAps.map(app => { var appObj = coreState.getAppObjByUuid(app.uuid), name = appObj._options.name, proc = appObj._processInfo || defaultProc; return { cpuUsage: proc.getCpuUsage(), name: name, nonPagedPoolUsage: proc.getNonPagedPoolUsage(), pageFaultCount: proc.getPageFaultCount(), pagedPoolUsage: proc.getPagedPoolUsage(), pagefileUsage: proc.getPagefileUsage(), peakNonPagedPoolUsage: proc.getPeakNonPagedPoolUsage(), peakPagedPoolUsage: proc.getPeakPagedPoolUsage(), peakPagefileUsage: proc.getPeakPagefileUsage(), peakWorkingSetSize: proc.getPeakWorkingSetSize(), processId: appObj.mainWindow.webContents.processId, uuid: appObj.uuid, workingSetSize: proc.getWorkingSetSize() }; }); return processList; }, getProxySettings: function () { return { config: coreState.getManifestProxySettings(), system: session.defaultSession.getProxySettings() }; }, getRemoteConfig: function (url, callback, errorCallback) { cached_resource_fetcher_1.fetchReadFile(url, true) .then(callback) .catch(errorCallback); }, getVersion: function () { return process.versions['openfin']; }, getRuntimeInfo: function (identity) { const { port, securityRealm, version } = portDiscovery.getPortInfoByArgs(coreState.argo, socketServer.getPort()); const manifestUrl = coreState.getConfigUrlByUuid(identity.uuid); const architecture = process.arch; const cachePath = electronApp.getPath('userData'); return { manifestUrl, port, securityRealm, version, architecture, cachePath }; }, getRvmInfo: function (identity, callback, errorCallback) { let appObject = coreState.getAppObjByUuid(identity.uuid); let sourceUrl = (appObject || {})._configUrl || ''; let RvmInfoFetcher = require('../rvm/runtime_initiated_topics/rvm_info.js'); RvmInfoFetcher.fetch(sourceUrl, callback, errorCallback); }, launchExternalProcess: function (identity, options, errDataCallback) { options.srcUrl = coreState.getConfigUrlByUuid(identity.uuid); ProcessTracker.launch(identity, options, errDataCallback); }, monitorExternalProcess: function (identity, options, callback, errorCallback) { const payload = ProcessTracker.monitor(identity, Object.assign({ monitor: true }, options)); if (payload) { callback(payload); } else { errorCallback('Error monitoring external process, pid: ' + options.pid); } }, log: function (level, message) { return log.writeToLog(level, message, false); }, setMinLogLevel: function (level) { try { const levelAsString = String(level); const mappedLevel = log.logLevelMappings.get(levelAsString); if (mappedLevel === undefined) { throw new Error(`Invalid logging level: ${level}`); } electronApp.setMinLogLevel(mappedLevel); } catch (e) { return e; } }, debugLog: function (level, message) { return log.writeToLog(level, message, true); }, openUrlWithBrowser: function (url) { shell.openExternal(url); }, readRegistryValue: function (rootKey, subkey, value) { const registryPayload = electronApp.readRegistryValue(rootKey, subkey, value); if (registryPayload && registryPayload.error) { throw new Error(registryPayload.error); } return registryPayload; }, releaseExternalProcess: function (processUuid) { ProcessTracker.release(processUuid); }, removeEventListener: function (type, listener) { of_events_1.default.removeListener(route_1.default.system(type), listener); }, showDeveloperTools: function (applicationUuid, windowName) { let winName, openfinWindow; if (!windowName) { winName = applicationUuid; } else { winName = windowName; } openfinWindow = coreState.getWindowByUuidName(applicationUuid, winName); if (openfinWindow && openfinWindow.browserWindow) { openfinWindow.browserWindow.openDevTools(); } }, showChromeNotificationCenter: function () { }, startCrashReporter: function (identity, options) { const configUrl = coreState.argo['startup-url'] || coreState.argo['config']; const reporterOptions = Object.assign({ configUrl }, options); log.setToVerbose(); return crashReporter.startOFCrashReporter(reporterOptions); }, terminateExternalProcess: function (processUuid, timeout = 3000, child = false) { let status = ProcessTracker.terminate(processUuid, timeout, child); let result; if (status === 0) { result = 'failed'; } else if (status === 1) { result = 'clean'; } else { result = 'terminated'; } return { result }; }, updateProxySettings: function (type, proxyAddress, proxyPort) { return coreState.setManifestProxySettings({ type: type, proxyAddress: proxyAddress, proxyPort: proxyPort }); }, setCookie: function (opts, callback, errorCallback) { let timeToLive = -1; if (typeof opts.ttl === 'number' && opts.ttl !== 0) { timeToLive = opts.ttl / 1000; } opts.expirationDate = Date.now() + timeToLive; opts.session = opts.session ? opts.session : opts.httpOnly; session.defaultSession.cookies.set(opts, function (error) { if (!error) { callback(); } else { errorCallback(error); } }); }, getCookies: function (opts, callback, errorCallback) { const { url, name } = opts; if (url && url.length > 0 && name && name.length > 0) { session.defaultSession.cookies.get({ url, name }, (error, cookies) => { if (error) { log.writeToLog(1, `cookies.get error ${error}`, true); errorCallback(error); } else if (cookies.length > 0) { const data = cookies.filter(cookie => !cookie.httpOnly).map(cookie => { return { name: cookie.name, expirationDate: cookie.expirationDate, path: cookie.path, domain: cookie.domain }; }); log.writeToLog(1, `cookies filtered ${data.length}`, true); if (data.length > 0) { callback(data); } else { errorCallback(`Cookie not found ${name}`); } } else { log.writeToLog(1, `cookies result ${cookies.length}`, true); errorCallback(`Cookie not found ${name}`); } }); } else { errorCallback(`Error getting cookies`); } }, flushCookieStore: function (callback) { session.defaultSession.cookies.flushStore(callback); }, generateGUID: function () { return electronApp.generateGUID(); }, convertOptions: function (options) { return convertOptions.convertToElectron(options); }, getNearestDisplayRoot: function (point) { return MonitorInfo.getNearestDisplayRoot(point); }, raiseEvent: function (eventName, eventArgs) { return of_events_1.default.emit(eventName, eventArgs); }, raiseManyEvents: function (eventsIter) { for (let [eventName, args] of eventsIter) { of_events_1.default.emit(eventName, args); } }, downloadAsset: function (identity, asset, cb) { const srcUrl = coreState.getConfigUrlByUuid(identity.uuid); const downloadId = asset.downloadId; asset.args = asset.args || ''; const rvmMessage = { topic: 'app-assets', type: 'download-asset', appConfig: srcUrl, showRvmProgressDialog: false, asset: asset, downloadId: downloadId }; const publishSuccess = rvmBus.publish(rvmMessage, response => { if (response.error) { cb(new Error(response.error)); } else { cb(); } }); if (!publishSuccess) { cb(new Error('RVM Message failed.')); } }, downloadRuntime: function (identity, options, cb) { options.sourceUrl = coreState.getConfigUrlByUuid(identity.uuid); rvmBus.downloadRuntime(options, cb); }, getAllExternalApplications: function () { return external_application_1.ExternalApplication.getAllExternalConnctions().map(eApp => { return { uuid: eApp.uuid }; }); }, resolveUuid: function (identity, uuid, cb) { const externalConn = external_application_1.ExternalApplication.getAllExternalConnctions().find(c => c.uuid === uuid); const app = coreState.getAppObjByUuid(uuid); if (externalConn) { cb(null, { type: 'external-app', uuid: externalConn.uuid }); } else if (app) { cb(null, { type: 'application', uuid: app.uuid }); } else { cb(new Error('uuid not found.')); } }, downloadPreloadScripts: function (identity, preloadScripts) { return preload_scripts_1.downloadScripts(identity, preloadScripts); }, getPreloadScripts: function (identity) { return preload_scripts_1.loadScripts(identity); } }; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); let fs = require('fs'); let path = require('path'); let url = require('url'); let electron = require('electron'); let BrowserWindow = electron.BrowserWindow; let electronApp = electron.app; let Menu = electron.Menu; let nativeImage = electron.nativeImage; let _ = require('underscore'); const crypto = require('crypto'); const Rx = require("rx"); let animations = require('../animations.js'); const authentication_delegate_1 = require("../authentication_delegate"); const bounds_changed_state_tracker_1 = require("../bounds_changed_state_tracker"); const utils_1 = require("../utils"); let convertOptions = require('../convert_options.js'); let coreState = require('../core_state.js'); let ExternalWindowEventAdapter = require('../external_window_event_adapter.js'); const cached_resource_fetcher_1 = require("../cached_resource_fetcher"); let log = require('../log'); const of_events_1 = require("../of_events"); const subscription_manager_1 = require("../subscription_manager"); const window_groups_1 = require("../window_groups"); const utils_2 = require("../rvm/utils"); const navigation_validation_1 = require("../navigation_validation"); const safe_int_1 = require("../../common/safe_int"); const route_1 = require("../../common/route"); const frame_1 = require("./frame"); const system_1 = require("./system"); const main_1 = require("../../common/main"); const shapes_1 = require("../../shapes"); const errors_1 = require("../../common/errors"); const subscriptionManager = new subscription_manager_1.default(); const isWin32 = process.platform === 'win32'; const windowPosCacheFolder = 'winposCache'; const WindowsMessages = { WM_KEYDOWN: 0x0100, WM_KEYUP: 0x0101, WM_SYSKEYDOWN: 0x0104, WM_SYSKEYUP: 0x0105, }; let Window = {}; const disabledFrameRef = new Map(); let browserWindowEventMap = { 'api-injection-disabled': { topic: 'api-injection-disabled' }, 'api-injection-failed': { topic: 'api-injection-failed' }, 'blur': { topic: 'blurred', }, 'synth-bounds-change': { topic: 'bounds-changing', decorator: boundsChangeDecorator }, 'close': { topic: 'close-requested', decorator: closeRequestedDecorator }, 'disabled-frame-bounds-changed': { topic: 'disabled-frame-bounds-changed', decorator: disabledFrameBoundsChangeDecorator }, 'disabled-frame-bounds-changing': { topic: 'disabled-frame-bounds-changing', decorator: disabledFrameBoundsChangeDecorator }, 'focus': { topic: 'focused', }, 'opacity-changed': { decorator: opacityChangedDecorator }, 'user-movement-disabled': { topic: 'frame-disabled' }, 'user-movement-enabled': { topic: 'frame-enabled' }, 'visibility-changed': { topic: 'hidden', decorator: visibilityChangedDecorator }, 'maximize': { topic: 'maximized' }, 'minimize': { topic: 'minimized' }, 'restore': { topic: 'restored' }, 'resize': { topic: 'bounds-changing', decorator: boundsChangeDecorator }, 'unmaximize': { topic: 'restored' } }; let webContentsEventMap = { 'did-get-response-details': { topic: 'resource-response-received', decorator: responseReceivedDecorator }, 'did-fail-load': { topic: 'resource-load-failed', decorator: loadFailedDecorator } }; function genWindowKey(identity) { return `${identity.uuid}-${identity.name}`; } let optionSetters = { contextMenu: function (newVal, browserWin) { let contextMenuBool = !!newVal; optionSetters['contextMenuSettings']({ enable: contextMenuBool }, browserWin); }, contextMenuSettings: function (newVal, browserWin) { if (!newVal || (typeof newVal.enable !== 'undefined' && typeof newVal.enable !== 'boolean') || (typeof newVal.devtools !== 'undefined' && typeof newVal.devtools !== 'boolean') || (typeof newVal.reload !== 'undefined' && typeof newVal.reload !== 'boolean')) { return; } const val = Object.assign({}, getOptFromBrowserWin('contextMenuSettings', browserWin), newVal); setOptOnBrowserWin('contextMenuSettings', val, browserWin); browserWin.setMenu(null); browserWin.webContents.updateContextMenuSettings(val); }, customData: function (newVal, browserWin) { setOptOnBrowserWin('customData', newVal, browserWin); }, frame: function (newVal, browserWin) { let frameBool = !!newVal; setOptOnBrowserWin('frame', frameBool, browserWin); browserWin.setHasFrame(frameBool); if (!frameBool) { let cornerRounding = getOptFromBrowserWin('cornerRounding', browserWin, { width: 0, height: 0 }); browserWin.setRoundedCorners(cornerRounding.width, cornerRounding.height); applyAdditionalOptionsToWindowOnVisible(browserWin, () => { if (!browserWin.isDestroyed()) { let resizeRegion = getOptFromBrowserWin('resizeRegion', browserWin, {}); resizeRegion = Object.assign({}, { size: shapes_1.DEFAULT_RESIZE_REGION_SIZE, bottomRightCorner: shapes_1.DEFAULT_RESIZE_REGION_BOTTOM_RIGHT_CORNER }, resizeRegion); browserWin.setResizeRegion(resizeRegion.size); browserWin.setResizeRegionBottomRight(resizeRegion.bottomRightCorner); } }); } else { setTaskbar(browserWin, true); } applyAdditionalOptionsToWindowOnVisible(browserWin, () => { if (!browserWin.isDestroyed()) { let resizeRegion = getOptFromBrowserWin('resizeRegion', browserWin, {}); const sides = Object.assign({}, shapes_1.DEFAULT_RESIZE_SIDES, resizeRegion.sides); browserWin.setResizeSides(sides.top, sides.right, sides.bottom, sides.left); } }); }, alphaMask: function (newVal, browserWin) { if (!newVal || typeof newVal.red !== 'number' || typeof newVal.green !== 'number' || typeof newVal.blue !== 'number') { return; } applyAdditionalOptionsToWindowOnVisible(browserWin, () => { if (!browserWin.isDestroyed()) { browserWin.setAlphaMask(newVal.red, newVal.green, newVal.blue); } }); setOptOnBrowserWin('alphaMask', newVal, browserWin); }, hideOnClose: function (newVal, browserWin) { let newHideOnCloseBool = !!newVal; let oldHideOnCloseBool = getOptFromBrowserWin('hideOnClose', browserWin, false); let uuid = browserWin._options.uuid; let name = browserWin._options.name; let openfinWindow = Window.wrap(uuid, name); let hideOnCloseListener = openfinWindow.hideOnCloseListener; let closeEventString = route_1.default.window('close-requested', uuid, name); if (newHideOnCloseBool && !oldHideOnCloseBool) { of_events_1.default.on(closeEventString, hideOnCloseListener); } else if (!newHideOnCloseBool && oldHideOnCloseBool) { of_events_1.default.removeListener(closeEventString, hideOnCloseListener); } setOptOnBrowserWin('hideOnClose', newHideOnCloseBool, browserWin); }, alwaysOnTop: function (newVal, browserWin) { var onTopBool = !!newVal; browserWin.setAlwaysOnTop(onTopBool); setOptOnBrowserWin('alwaysOnTop', onTopBool, browserWin); }, cornerRounding: function (newVal, browserWin) { if (!newVal || typeof newVal.width !== 'number' || typeof newVal.height !== 'number') { return; } let frame = getOptFromBrowserWin('frame', browserWin, true); if (!frame) { browserWin.setRoundedCorners(newVal.width, newVal.height); } setOptOnBrowserWin('cornerRounding', newVal, browserWin); }, maxHeight: function (newVal, browserWin) { var maxWidth = getOptFromBrowserWin('maxWidth', browserWin, -1); browserWin.setMaximumSize(maxWidth, newVal); setOptOnBrowserWin('maxHeight', newVal, browserWin); }, maxWidth: function (newVal, browserWin) { var maxHeight = getOptFromBrowserWin('maxHeight', browserWin, -1); browserWin.setMaximumSize(newVal, maxHeight); setOptOnBrowserWin('maxWidth', newVal, browserWin); }, maximizable: function (newVal, browserWin) { let maxBool = !!newVal; browserWin.setMaximizable(maxBool); setOptOnBrowserWin('maximizable', maxBool, browserWin); }, minimizable: function (newVal, browserWin) { let minBool = !!newVal; browserWin.setMinimizable(minBool); setOptOnBrowserWin('minimizable', minBool, browserWin); }, minHeight: function (newVal, browserWin) { var minWidth = getOptFromBrowserWin('minWidth', browserWin, -1); browserWin.setMinimumSize(minWidth, newVal); setOptOnBrowserWin('minHeight', newVal, browserWin); }, minWidth: function (newVal, browserWin) { var minHeight = getOptFromBrowserWin('minHeight', browserWin, -1); browserWin.setMinimumSize(newVal, minHeight); setOptOnBrowserWin('minWidth', newVal, browserWin); }, opacity: function (newVal, browserWin) { if (typeof newVal !== 'number') { return; } let frame = getOptFromBrowserWin('frame', browserWin, true); if (frame) { console.log('Opacity only supported on frameless windows'); } let opacity = newVal; opacity = opacity < 0 ? 0 : opacity; opacity = opacity > 1 ? 1 : opacity; applyAdditionalOptionsToWindowOnVisible(browserWin, () => { if (!browserWin.isDestroyed()) { browserWin.setOpacity(opacity); } }); setOptOnBrowserWin('opacity', opacity, browserWin); }, resizable: function (newVal, browserWin) { var resizeBool = !!newVal; browserWin.setResizable(resizeBool); setOptOnBrowserWin('resizable', resizeBool, browserWin); }, icon: function (newVal, browserWin) { if (typeof newVal !== 'string') { return; } setOptOnBrowserWin('icon', newVal, browserWin); setTaskbarIcon(browserWin, getWinOptsIconUrl(browserWin._options)); }, taskbarIcon: function (newVal, browserWin) { if (typeof newVal !== 'string') { return; } setOptOnBrowserWin('taskbarIcon', newVal, browserWin); setTaskbarIcon(browserWin, getWinOptsIconUrl(browserWin._options)); }, applicationIcon: function (newVal, browserWin) { if (typeof newVal !== 'string') { return; } setOptOnBrowserWin('applicationIcon', newVal, browserWin); setTaskbarIcon(browserWin, getWinOptsIconUrl(browserWin._options)); }, resizeRegion: function (newVal, browserWin) { if (newVal) { if (typeof newVal.size === 'number' && typeof newVal.bottomRightCorner === 'number') { applyAdditionalOptionsToWindowOnVisible(browserWin, () => { if (!browserWin.isDestroyed()) { let frame = getOptFromBrowserWin('frame', browserWin, true); if (!frame) { browserWin.setResizeRegion(newVal.size); browserWin.setResizeRegionBottomRight(newVal.bottomRightCorner); } } }); } if (typeof newVal.sides === 'object') { applyAdditionalOptionsToWindowOnVisible(browserWin, () => { if (!browserWin.isDestroyed()) { const sides = Object.assign({}, shapes_1.DEFAULT_RESIZE_SIDES, newVal.sides); browserWin.setResizeSides(sides.top, sides.right, sides.bottom, sides.left); } }); } setOptOnBrowserWin('resizeRegion', newVal, browserWin); } }, aspectRatio: function (newVal, browserWin) { if (typeof (newVal) !== 'number') { return; } browserWin.setAspectRatio(newVal); setOptOnBrowserWin('aspectRatio', newVal, browserWin); }, hasLoaded: function (newVal, browserWin) { if (typeof (newVal) === 'boolean') { browserWin._options.hasLoaded = newVal; } }, showTaskbarIcon: function (newVal, browserWin) { let showTaskbarIconBool = !!newVal; setOptOnBrowserWin('showTaskbarIcon', showTaskbarIconBool, browserWin); browserWin.setSkipTaskbar(!showTaskbarIconBool); } }; Window.create = function (id, opts) { let name = opts.name; let uuid = opts.uuid; let identity = { name, uuid }; let baseOpts; let browserWindow; let webContents; let _options; let _boundsChangedHandler; let groupUuid = null; let hideReason = 'hide'; let hideOnCloseListener = () => { let openfinWindow = Window.wrap(uuid, name); openfinWindow.hideReason = 'hide-on-close'; browserWindow.hide(); }; const ofUnloadedHandler = (eventObj, url, isReload) => { if (isReload) { emitReloadedEvent({ uuid, name }, url); } of_events_1.default.emit(route_1.default.window('unload', uuid, name, false), identity); of_events_1.default.emit(route_1.default.window('init-subscription-listeners'), identity); of_events_1.default.emit(route_1.default.window('openfin-diagnostic/unload', uuid, name, true), url); }; let _externalWindowEventAdapter; if (!opts._noregister) { browserWindow = BrowserWindow.fromId(id); browserWindow.webContents.registerIframe = (frameName, frameRoutingId) => { electronApp.vlog(1, `registerIframe ${frameName} ${frameRoutingId}`); const parentFrameId = id; const frameInfo = { name: frameName, uuid, parentFrameId, parent: { uuid, name }, frameRoutingId, entityType: 'iframe' }; winObj.frames.set(frameName, frameInfo); }; browserWindow.webContents.unregisterIframe = (closedFrameName, frameRoutingId) => { electronApp.vlog(1, `unregisterIframe ${frameRoutingId} ${closedFrameName}`); const frameName = closedFrameName || name; const frameInfo = winObj.frames.get(closedFrameName); const entityType = frameInfo ? 'iframe' : 'window'; const payload = { uuid, name, frameName, entityType }; winObj.frames.delete(closedFrameName); of_events_1.default.emit(route_1.default.frame('disconnected', uuid, closedFrameName), payload); of_events_1.default.emit(route_1.default.window('frame-disconnected', uuid, name), payload); }; webContents = browserWindow.webContents; if (coreState.getAppObjByUuid(identity.uuid)._options.customWindowAlert) { handleCustomAlerts(id, opts); } baseOpts = coreState.getMainWindowOptions(id) || {}; _options = convertOptions.convertToElectron(Object.assign({}, baseOpts, opts)); _options.taskbarIconGroup = _options.taskbarIconGroup || baseOpts.uuid; _options.frameConnect = _options.frameConnect || baseOpts.frameConnect || 'last'; _options.toShowOnRun = opts.toShowOnRun; _options.hasLoaded = false; uuid = _options.uuid; name = _options.name; const OF_WINDOW_UNLOADED = 'of-window-navigation'; browserWindow._options = _options; setTaskbar(browserWindow); applyAdditionalOptionsToWindow(browserWindow); _boundsChangedHandler = new bounds_changed_state_tracker_1.default(uuid, name, browserWindow); if (browserWindow.isExternalWindow()) { _externalWindowEventAdapter = new ExternalWindowEventAdapter(browserWindow); } let windowTeardown = createWindowTearDown(identity, id, browserWindow, _boundsChangedHandler); browserWindow.on('close', (event) => { let ofWindow = Window.wrap(uuid, name); let closeEventString = route_1.default.window('close-requested', uuid, name); let listenerCount = of_events_1.default.listenerCount(closeEventString); if (listenerCount && !ofWindow.forceClose && !browserWindow.isExternalWindow()) { if (!browserWindow.isDestroyed()) { event.preventDefault(); return; } } of_events_1.default.emit(route_1.default.window('synth-close', uuid, name), { name, uuid, topic: 'window', type: 'synth-close' }); browserWindow.webContents.removeAllListeners('page-favicon-updated'); of_events_1.default.removeAllListeners(closeEventString); }); browserWindow.once('will-close', () => { const type = 'closing'; windowTeardown() .then(() => log.writeToLog('info', `Window tear down complete ${uuid} ${name}`)) .catch(err => { log.writeToLog('info', `Error while tearing down ${uuid} ${name}`); log.writeToLog('info', err); }); of_events_1.default.emit(route_1.default.window(type, uuid, name), { topic: 'window', type: type, uuid, name }); }); webContents.once('close', () => { webContents.removeAllListeners(); }); const isMainWindow = (uuid === name); const emitToAppIfMainWin = (type, payload) => { of_events_1.default.emit(route_1.default.window(type, uuid, name), Object.assign({ topic: 'window', type, uuid, name }, payload)); if (isMainWindow) { of_events_1.default.emit(route_1.default.application(type, uuid), Object.assign({ topic: 'application', type, uuid }, payload)); } }; webContents.on('crashed', (event, killed, terminationStatus) => { emitToAppIfMainWin('crashed', { reason: terminationStatus }); const closeRequested = route_1.default.window('close-requested', uuid, name); of_events_1.default.removeAllListeners(closeRequested); const showRequested = route_1.default.window('show-requested', uuid, name); of_events_1.default.removeAllListeners(showRequested); if (isMainWindow) { coreState.setAppRunningState(uuid, false); const message = `A crash occured in the renderer process of the ` + `application with the UUID "${uuid}"`; const title = errors_1.ERROR_TITLE_RENDERER_CRASH; const type = errors_1.ERROR_BOX_TYPES.RENDERER_CRASH; const args = { message, title, type }; errors_1.showErrorBox(args); } }); browserWindow.on('responsive', () => { emitToAppIfMainWin('responding'); }); browserWindow.on('unresponsive', () => { emitToAppIfMainWin('not-responding'); }); let mapEvents = function (eventMap, eventEmitter) { Object.keys(eventMap).forEach(evnt => { var mappedMeta = eventMap[evnt]; var mappedTopic = mappedMeta.topic || ''; var electronEventListener = function () { if (!Window.wrap(uuid, name)) { return; } let payload = { name, uuid, topic: 'window', type: mappedTopic }; let decoratorFn = mappedMeta.decorator || noOpDecorator; if (decoratorFn(payload, arguments)) { of_events_1.default.emit(route_1.default.window(payload.type, uuid, name), payload); if (evnt === 'user-movement-disabled' || evnt === 'user-movement-enabled') { let newPayload = _.clone(payload); newPayload.type = evnt; of_events_1.default.emit(route_1.default.window(newPayload.type, uuid, name), newPayload); } if (evnt === 'disabled-frame-bounds-changed' || evnt === 'disabled-frame-bounds-changing') { const newEventType = evnt === 'disabled-frame-bounds-changed' ? 'disabled-movement-bounds-changed' : 'disabled-movement-bounds-changing'; let newPayload = _.clone(payload); newPayload.type = newEventType; of_events_1.default.emit(route_1.default.window(newPayload.type, uuid, name), newPayload); } } }; eventEmitter.on(evnt, electronEventListener); }); }; mapEvents(browserWindowEventMap, browserWindow); mapEvents(webContentsEventMap, webContents); if (getOptFromBrowserWin('hideOnClose', browserWindow, false)) { let closeEventString = route_1.default.window('close-requested', uuid, name); of_events_1.default.on(closeEventString, hideOnCloseListener); } let groupChangedEventString = 'group-changed'; let groupChangedListener = (event) => { var _win = coreState.getWindowByUuidName(uuid, name) || {}; var _groupUuid = _win.groupUuid || null; if (event.groupUuid === _groupUuid || _win.uuid === void 0) { var payload = event.payload; payload.name = name; payload.uuid = _win.app_uuid || event.uuid; if (payload.reason === 'disband') { payload.memberOf = 'nothing'; } else if (payload.reason === 'leave') { payload.memberOf = payload.sourceWindowName === name ? 'nothing' : 'source'; } else { var isSource = _.find(payload.sourceGroup, { windowName: name }); payload.memberOf = isSource ? 'source' : 'target'; } of_events_1.default.emit(route_1.default.window(payload.type, uuid, name), payload); } }; let groupChangedUnsubscribe = () => { window_groups_1.default.removeListener(groupChangedEventString, groupChangedListener); }; window_groups_1.default.on(groupChangedEventString, groupChangedListener); subscriptionManager.registerSubscription(groupChangedUnsubscribe, identity, groupChangedEventString); const navValidator = navigation_validation_1.navigationValidator(uuid, name, id); navigation_validation_1.validateNavigation(webContents, identity, navValidator); let startLoadingSubscribe = (event, url) => { of_events_1.default.emit(route_1.default.application('window-start-load', uuid), { name, uuid, url }); }; let startLoadingString = 'did-start-loading'; webContents.on(startLoadingString, startLoadingSubscribe); let startLoadingUnsubscribe = () => { webContents.removeListener(startLoadingString, startLoadingSubscribe); }; subscriptionManager.registerSubscription(startLoadingUnsubscribe, identity, startLoadingString); let documentLoadedSubscribe = (event, isMain, documentName) => { if (isMain && uuid === name) { of_events_1.default.emit(route_1.default.application('ready', uuid), { type: 'ready', uuid }); } of_events_1.default.emit(route_1.default.application('window-end-load', uuid), { name, uuid, isMain, documentName }); }; let documentLoadedString = 'document-loaded'; webContents.on(documentLoadedString, documentLoadedSubscribe); let documentLoadedUnsubscribe = () => { webContents.removeListener(documentLoadedString, documentLoadedSubscribe); }; subscriptionManager.registerSubscription(documentLoadedUnsubscribe, identity, documentLoadedString); of_events_1.default.emit(route_1.default.window('init-subscription-listeners'), { name, uuid }); let constructorCallbackMessage = { success: true }; let emitErrMessage = (errCode) => { let chromeErrLink = 'https://cs.chromium.org/chromium/src/net/base/net_error_list.h'; constructorCallbackMessage.success = false; constructorCallbackMessage.data = { networkErrorCode: errCode, message: `error #${errCode}. See ${chromeErrLink} for details` }; of_events_1.default.emit(route_1.default.window('fire-constructor-callback', uuid, name), constructorCallbackMessage); }; let resourceResponseReceivedHandler, resourceLoadFailedHandler; let resourceResponseReceivedEventString = route_1.default.window('resource-response-received', uuid, name); let resourceLoadFailedEventString = route_1.default.window('resource-load-failed', uuid, name); let httpResponseCode = null; resourceResponseReceivedHandler = (details) => { httpResponseCode = details.httpResponseCode; of_events_1.default.removeListener(resourceLoadFailedEventString, resourceLoadFailedHandler); }; resourceLoadFailedHandler = (failed) => { if (failed.errorCode === -3) { electronApp.vlog(1, `ignoring net error -3 for ${failed.validatedURL}`); } else { emitErrMessage(failed.errorCode); of_events_1.default.removeListener(resourceResponseReceivedEventString, resourceResponseReceivedHandler); } }; const apiInjectionObserver = Rx.Observable.create((observer) => { if (opts.url === 'about:blank') { webContents.once('did-finish-load', () => { webContents.on(OF_WINDOW_UNLOADED, ofUnloadedHandler); constructorCallbackMessage.data = { httpResponseCode }; observer.next(constructorCallbackMessage); }); } else { of_events_1.default.once(resourceResponseReceivedEventString, resourceResponseReceivedHandler); of_events_1.default.once(resourceLoadFailedEventString, resourceLoadFailedHandler); of_events_1.default.once(route_1.default.window('connected', uuid, name), () => { webContents.on(OF_WINDOW_UNLOADED, ofUnloadedHandler); constructorCallbackMessage.data = { httpResponseCode, apiInjected: true }; observer.next(constructorCallbackMessage); }); of_events_1.default.once(route_1.default.window('api-injection-failed', uuid, name), () => { electronApp.vlog(1, `api-injection-failed ${uuid}-${name}`); if (_options.autoShow) { browserWindow.show(); } constructorCallbackMessage.data = { httpResponseCode, apiInjected: false }; observer.next(constructorCallbackMessage); }); of_events_1.default.once(route_1.default.window('api-injection-disabled', uuid, name), () => { electronApp.vlog(1, `api-injection-disabled ${uuid}-${name}`); browserWindow.show(); constructorCallbackMessage.data = { httpResponseCode, apiInjected: false }; observer.next(constructorCallbackMessage); }); } }); const windowPositioningObserver = Rx.Observable.create(observer => { if (!_options.saveWindowState) { observer.next(); } else if (_options.waitForPageLoad) { browserWindow.once('ready-to-show', () => { restoreWindowPosition(identity, () => observer.next()); }); } else { restoreWindowPosition(identity, () => observer.next()); } }); const subscription = Rx.Observable.zip(apiInjectionObserver, windowPositioningObserver).subscribe((event) => { const constructorCallbackMessage = event[0]; if (_options.autoShow || _options.toShowOnRun) { Window.show(identity); } of_events_1.default.emit(route_1.default.window('fire-constructor-callback', uuid, name), constructorCallbackMessage); subscription.dispose(); }); } var winObj = { name, uuid, _options, id, browserWindow, groupUuid, hideReason, hideOnCloseListener, forceClose: false, app_uuid: uuid, children: [], frames: new Map(), _window: browserWindow }; const prepareConsoleMessageForRVM = (event, level, message, lineNo, sourceId) => { const printDebugLogs = (coreState.argo['v'] >= 1); if ((level === -1 && !printDebugLogs) || level === 0 || level === 1) { event.preventDefault(); } const app = coreState.getAppByUuid(identity.uuid); if (!app) { electronApp.vlog(2, `Error: could not get app object for app with uuid: ${identity.uuid}`); return; } if (!app._options || !app._options.enableAppLogging) { return; } setTimeout(() => { const appConfigUrl = coreState.getConfigUrlByUuid(identity.uuid); if (!appConfigUrl) { electronApp.vlog(2, `Error: could not get manifest url for app with uuid: ${identity.uuid}`); return; } function checkPrependLeadingZero(num, length) { let str = String(num); while (str.length < length) { str = '0' + str; } return str; } const date = new Date(); const year = String(date.getFullYear()); const month = checkPrependLeadingZero(date.getMonth() + 1, 2); const day = checkPrependLeadingZero(date.getDate(), 2); const hour = checkPrependLeadingZero(date.getHours(), 2); const minute = checkPrependLeadingZero(date.getMinutes(), 2); const second = checkPrependLeadingZero(date.getSeconds(), 2); const millisecond = checkPrependLeadingZero(date.getMilliseconds(), 3); const timeStamp = `${year}-${month}-${day} ${hour}:${minute}:${second}.${millisecond}`; utils_2.addConsoleMessageToRVMMessageQueue({ level, message, appConfigUrl, timeStamp }, app._options.appLogFlushInterval); }, 1); }; webContents.on('console-message', prepareConsoleMessageForRVM); winObj.preloadScripts = (_options.preloadScripts || []); winObj.framePreloadScripts = {}; if (!coreState.getWinObjById(id)) { coreState.deregisterPendingWindowName(uuid, name); coreState.setWindowObj(id, winObj); of_events_1.default.emit(route_1.default.application('window-created', uuid), { topic: 'application', type: 'window-created', uuid, name }); } return winObj; }; Window.wrap = function (uuid, name) { return coreState.getWindowByUuidName(uuid, name); }; Window.connected = function () { }; Window.isEmbedded = function () { }; Window.addEventListener = function (identity, targetIdentity, type, listener) { let eventString = route_1.default.window(type, targetIdentity.uuid, targetIdentity.name); let errRegex = /^Attempting to call a function in a renderer window that has been closed or released/; let unsubscribe, safeListener, browserWinIsDead; safeListener = (...args) => { try { listener.call(null, ...args); } catch (err) { browserWinIsDead = errRegex.test(err.message); if (browserWinIsDead) { of_events_1.default.removeListener(eventString, safeListener); } } }; electronApp.vlog(1, `addEventListener ${eventString}`); of_events_1.default.on(eventString, safeListener); unsubscribe = () => { of_events_1.default.removeListener(eventString, safeListener); }; return unsubscribe; }; Window.animate = function (identity, transitions, options = {}, callback = () => { }, errorCallback = () => { }) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { callback(); return; } let animationMeta = transitions || {}; let animationTween = (options && options.tween) || 'ease-in-out'; animationMeta.interrupt = (options || {}).interrupt; if (typeof animationMeta.interrupt !== 'boolean') { animationMeta.interrupt = true; } animations.getAnimationHandler().add(browserWindow, animationMeta, animationTween, callback, errorCallback); }; Window.blur = function (identity) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } browserWindow.blur(); }; Window.bringToFront = function (identity) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } browserWindow.bringToFront(); }; Window.close = function (identity, force, callback = () => { }) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { callback(); return; } let payload = { force }; let defaultAction = () => { if (!browserWindow.isDestroyed()) { let openfinWindow = Window.wrap(identity.uuid, identity.name); openfinWindow.forceClose = true; browserWindow.close(); } }; of_events_1.default.once(route_1.default.window('closed', identity.uuid, identity.name), () => { callback(); }); handleForceActions(identity, force, 'close-requested', payload, defaultAction); }; function disabledFrameUnsubDecorator(identity) { const windowKey = genWindowKey(identity); return function () { let refCount = disabledFrameRef.get(windowKey) || 0; if (refCount > 1) { disabledFrameRef.set(windowKey, --refCount); } else { Window.enableUserMovement(identity); } }; } Window.disableUserMovement = function (requestorIdentity, windowIdentity) { const browserWindow = getElectronBrowserWindow(windowIdentity); const windowKey = genWindowKey(windowIdentity); if (!browserWindow) { return; } let dframeRefCount = disabledFrameRef.get(windowKey) || 0; disabledFrameRef.set(windowKey, ++dframeRefCount); subscriptionManager.registerSubscription(disabledFrameUnsubDecorator(windowIdentity), requestorIdentity, `disable-frame-${windowKey}`); electronApp.vlog(1, `disableUserMovement setUserMovementEnabled false ${windowIdentity.name} `); browserWindow.setUserMovementEnabled(false); }; Window.embed = function (identity, parentHwnd) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } if (isWin32) { browserWindow.setMessageObserver(WindowsMessages.WM_KEYDOWN, parentHwnd); browserWindow.setMessageObserver(WindowsMessages.WM_KEYUP, parentHwnd); browserWindow.setMessageObserver(WindowsMessages.WM_SYSKEYDOWN, parentHwnd); browserWindow.setMessageObserver(WindowsMessages.WM_SYSKEYUP, parentHwnd); } of_events_1.default.emit(route_1.default.window('embedded', identity.uuid, identity.name), { topic: 'window', type: 'window-embedded', name: identity.name, uuid: identity.uuid }); }; Window.enableUserMovement = function (identity) { const windowKey = genWindowKey(identity); let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } let dframeRefCount = disabledFrameRef.get(windowKey) || 0; disabledFrameRef.set(windowKey, --dframeRefCount); electronApp.vlog(1, `enableUserMovement setUserMovementEnabled true ${identity.name} `); browserWindow.setUserMovementEnabled(true); }; Window.executeJavascript = function (identity, code, callback = () => { }) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { callback(new Error(`Could not locate window '${identity.name}'`)); return; } browserWindow.webContents.executeJavaScript(code, true, (result) => { callback(undefined, result); }); }; Window.flash = function (identity) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } browserWindow.flashFrame(true); }; Window.stopFlashing = function (identity) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } browserWindow.flashFrame(false); }; Window.focus = function (identity) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } browserWindow.focus(); }; Window.getAllFrames = function (identity) { const openfinWindow = coreState.getWindowByUuidName(identity.uuid, identity.name); if (!openfinWindow) { return []; } const framesArr = [coreState.getInfoByUuidFrame(identity)]; const subFrames = []; for (let [, info] of openfinWindow.frames) { subFrames.push(new frame_1.FrameInfo(info)); } return framesArr.concat(subFrames); }; Window.getBounds = function (identity) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return { height: 0, left: -1, top: -1, width: 0, right: -1, bottom: -1 }; } let bounds = browserWindow.getBounds(); return { height: bounds.height, left: bounds.x, top: bounds.y, width: bounds.width, right: bounds.width + bounds.x, bottom: bounds.height + bounds.y }; }; Window.getGroup = function (identity) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return []; } let openfinWindow = Window.wrap(identity.uuid, identity.name); return window_groups_1.default.getGroup(openfinWindow.groupUuid); }; Window.getWindowInfo = function (identity) { const browserWindow = getElectronBrowserWindow(identity, 'get info for'); const { preloadScripts } = Window.wrap(identity.uuid, identity.name); const webContents = browserWindow.webContents; const windowInfo = { canNavigateBack: webContents.canGoBack(), canNavigateForward: webContents.canGoForward(), preloadScripts, title: webContents.getTitle(), url: webContents.getURL() }; return windowInfo; }; Window.getAbsolutePath = function (identity, path) { let browserWindow = getElectronBrowserWindow(identity, 'get URL for'); let windowURL = browserWindow.webContents.getURL(); return (path || path === 0) ? url.resolve(windowURL, path) : ''; }; Window.getNativeId = function (identity) { let browserWindow = getElectronBrowserWindow(identity, 'get ID for'); return browserWindow.nativeId; }; Window.getNativeWindow = function () { }; Window.getOptions = function (identity) { try { return getElectronBrowserWindow(identity, 'get options for')._options; } catch (e) { return system_1.System.getEntityInfo(identity); } }; Window.getParentApplication = function () { let app = coreState.getAppByWin(this.id); return app && app.appObj; }; Window.getParentWindow = function () { }; Window.setWindowPreloadState = function (identity, payload) { const { uuid, name } = identity; const { url, state, allDone } = payload; const updateTopic = allDone ? 'preload-scripts-state-changed' : 'preload-scripts-state-changing'; const frameInfo = coreState.getInfoByUuidFrame(identity); let openfinWindow; if (frameInfo.entityType === 'iframe') { openfinWindow = Window.wrap(frameInfo.parent.uuid, frameInfo.parent.name); } else { openfinWindow = Window.wrap(uuid, name); } if (!openfinWindow) { return log.writeToLog('info', `setWindowPreloadState missing openfinWindow ${uuid} ${name}`); } let { preloadScripts } = openfinWindow; if (!allDone) { if (frameInfo.entityType === 'iframe') { let frameState = openfinWindow.framePreloadScripts[name]; if (!frameState) { frameState = openfinWindow.framePreloadScripts[name] = []; } preloadScripts = frameState.find(e => e.url === url); if (!preloadScripts) { frameState.push(preloadScripts = { url }); } preloadScripts = [preloadScripts]; } else { preloadScripts = openfinWindow.preloadScripts.filter(e => e.url === url); } if (preloadScripts) { preloadScripts[0].state = state; } else { log.writeToLog('info', `setWindowPreloadState missing preloadState ${uuid} ${name} ${url} `); } } if (frameInfo.entityType === 'window') { of_events_1.default.emit(route_1.default.window(updateTopic, uuid, name), { name, uuid, preloadScripts }); } }; Window.getSnapshot = (opts) => { return new Promise((resolve, reject) => { const { identity, payload: { area } } = opts; const browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { const error = new Error(`Unknown window named '${identity.name}'`); return reject(error); } const callback = (img) => { const imageBase64 = img.toPNG().toString('base64'); resolve(imageBase64); }; if (typeof area === 'undefined') { return browserWindow.capturePage(callback); } if (!area || typeof area !== 'object' || typeof area.x !== 'number' || typeof area.y !== 'number' || typeof area.width !== 'number' || typeof area.height !== 'number') { const error = new Error(`Invalid shape of the snapshot's area.`); return reject(error); } browserWindow.capturePage(area, callback); }); }; Window.getState = function (identity) { let browserWindow = getElectronBrowserWindow(identity); if (browserWindow && browserWindow.isMinimized()) { return 'minimized'; } else if (browserWindow && browserWindow.isMaximized()) { return 'maximized'; } else { return 'normal'; } }; Window.hide = function (identity) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } browserWindow.hide(); }; Window.isNotification = function (name) { const noteGuidRegex = /^A21B62E0-16B1-4B10-8BE3-BBB6B489D862/; return noteGuidRegex.test(name); }; Window.isShowing = function (identity) { let browserWindow = getElectronBrowserWindow(identity); return !!(browserWindow && browserWindow.isVisible()); }; Window.joinGroup = function (identity, grouping) { return window_groups_1.default.joinGroup({ uuid: identity.uuid, name: identity.name }, { uuid: grouping.uuid, name: grouping.name }); }; Window.leaveGroup = function (identity) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } let openfinWindow = Window.wrap(identity.uuid, identity.name); return window_groups_1.default.leaveGroup(openfinWindow); }; Window.maximize = function (identity) { let browserWindow = getElectronBrowserWindow(identity, 'maximize'); let maximizable = getOptFromBrowserWin('maximizable', browserWindow, true); if (maximizable) { browserWindow.maximize(); } }; Window.mergeGroups = function (identity, grouping) { return window_groups_1.default.mergeGroups({ uuid: identity.uuid, name: identity.name }, { uuid: grouping.uuid, name: grouping.name }); }; Window.minimize = function (identity) { let browserWindow = getElectronBrowserWindow(identity, 'minimize'); let minimizable = getOptFromBrowserWin('minimizable', browserWindow, true); if (minimizable) { browserWindow.minimize(); } }; Window.moveBy = function (identity, deltaLeft, deltaTop) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } let currentBounds = browserWindow.getBounds(); let left = safe_int_1.toSafeInt(deltaLeft, 0); let top = safe_int_1.toSafeInt(deltaTop, 0); if (browserWindow.isMaximized()) { browserWindow.unmaximize(); } browserWindow.setBounds({ x: currentBounds.x + left, y: currentBounds.y + top, width: currentBounds.width, height: currentBounds.height }); }; Window.moveTo = function (identity, x, y) { const browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } const currentBounds = browserWindow.getBounds(); const safeX = safe_int_1.toSafeInt(x); const safeY = safe_int_1.toSafeInt(y); if (browserWindow.isMaximized()) { browserWindow.unmaximize(); } browserWindow.setBounds({ x: safeX, y: safeY, width: currentBounds.width, height: currentBounds.height }); }; Window.navigate = function (identity, url) { let browserWindow = getElectronBrowserWindow(identity, 'navigate'); browserWindow.webContents.loadURL(url); }; Window.navigateBack = function (identity) { let browserWindow = getElectronBrowserWindow(identity, 'navigate back'); browserWindow.webContents.goBack(); }; Window.navigateForward = function (identity) { let browserWindow = getElectronBrowserWindow(identity, 'navigate forward'); browserWindow.webContents.goForward(); }; Window.reload = function (identity, ignoreCache = false) { let browserWindow = getElectronBrowserWindow(identity, 'reload'); if (!ignoreCache) { browserWindow.webContents.reload(); } else { browserWindow.webContents.reloadIgnoringCache(); } }; Window.stopNavigation = function (identity) { let browserWindow = getElectronBrowserWindow(identity, 'stop navigating'); browserWindow.webContents.stop(); }; Window.removeEventListener = function (identity, type, listener) { let browserWindow = getElectronBrowserWindow(identity, 'remove event listener for'); of_events_1.default.removeListener(route_1.default.window(type, browserWindow.id), listener); }; Window.resizeBy = function (identity, deltaWidth, deltaHeight, anchor) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } if (browserWindow.isMaximized()) { browserWindow.unmaximize(); } let currentBounds = browserWindow.getBounds(); let newWidth = safe_int_1.toSafeInt(currentBounds.width + deltaWidth, currentBounds.width); let newHeight = safe_int_1.toSafeInt(currentBounds.height + deltaHeight, currentBounds.height); let boundsAnchor = calcBoundsAnchor(anchor, newWidth, newHeight, currentBounds); browserWindow.setBounds(utils_1.clipBounds({ x: boundsAnchor.x, y: boundsAnchor.y, width: newWidth, height: newHeight }, browserWindow)); }; Window.resizeTo = function (identity, newWidth, newHeight, anchor) { const browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } if (browserWindow.isMaximized()) { browserWindow.unmaximize(); } const currentBounds = browserWindow.getBounds(); newWidth = safe_int_1.toSafeInt(newWidth, currentBounds.width); newHeight = safe_int_1.toSafeInt(newHeight, currentBounds.height); const boundsAnchor = calcBoundsAnchor(anchor, newWidth, newHeight, currentBounds); browserWindow.setBounds(utils_1.clipBounds({ x: boundsAnchor.x, y: boundsAnchor.y, width: newWidth, height: newHeight }, browserWindow)); }; Window.restore = function (identity) { let browserWindow = getElectronBrowserWindow(identity, 'restore'); if (browserWindow.isMinimized()) { utils_1.windowSetBoundsToVisible(browserWindow); browserWindow.restore(); } else if (browserWindow.isMaximized()) { browserWindow.unmaximize(); } else { browserWindow.showInactive(); } }; Window.setAsForeground = function (identity) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } browserWindow.activate(); }; Window.setBounds = function (identity, left, top, width, height) { let browserWindow = getElectronBrowserWindow(identity, 'set window bounds for'); let bounds = browserWindow.getBounds(); if (browserWindow.isMaximized()) { browserWindow.unmaximize(); } browserWindow.setBounds(utils_1.clipBounds({ x: safe_int_1.toSafeInt(left, bounds.x), y: safe_int_1.toSafeInt(top, bounds.y), width: safe_int_1.toSafeInt(width, bounds.width), height: safe_int_1.toSafeInt(height, bounds.height) }, browserWindow)); }; Window.show = function (identity, force = false) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } let payload = {}; let defaultAction = () => { const dontShow = (browserWindow.isVisible() && (browserWindow.isMinimized() || browserWindow.isMaximized())); if (!dontShow) { browserWindow.showInactive(); } }; handleForceActions(identity, force, 'show-requested', payload, defaultAction); }; Window.showAt = function (identity, left, top, force = false) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } const safeLeft = safe_int_1.toSafeInt(left); const safeTop = safe_int_1.toSafeInt(top); let payload = { top: safeTop, left: safeLeft }; let defaultAction = () => { let currentBounds = browserWindow.getBounds(); if (browserWindow.isMaximized()) { browserWindow.unmaximize(); } browserWindow.setBounds({ x: safeLeft, y: safeTop, width: currentBounds.width, height: currentBounds.height }); if (!browserWindow.isMinimized()) { browserWindow.showInactive(); } }; handleForceActions(identity, force, 'show-requested', payload, defaultAction); }; Window.showMenu = function (identity, x, y, editable, hasSelectedText) { let browserWindow = getElectronBrowserWindow(identity); if (!browserWindow) { return; } const menuTemplate = []; if (editable) { menuTemplate.push({ label: 'Cut', click: () => { browserWindow.webContents.cut(); }, accelerator: 'CommandOrControl+X', enabled: hasSelectedText }); menuTemplate.push({ label: 'Copy', click: () => { browserWindow.webContents.copy(); }, accelerator: 'CommandOrControl+C', enabled: hasSelectedText }); menuTemplate.push({ label: 'Paste', click: () => { browserWindow.webContents.paste(); }, accelerator: 'CommandOrControl+V' }); menuTemplate.push({ label: 'Select all', click: () => { browserWindow.webContents.selectAll(); }, accelerator: 'CommandOrControl+A' }); menuTemplate.push({ type: 'separator' }); } menuTemplate.push({ label: 'Reload', click: () => { browserWindow.webContents.reloadIgnoringCache(); } }, { label: 'Reload app and restart children', click: () => { try { const Application = require('./application.js').Application; const app = Application.wrap(identity.uuid); Application.getChildWindows(identity).forEach(childWin => { Window.close({ name: childWin.name, uuid: childWin.uuid }, true); }); app.mainWindow.webContents.reloadIgnoringCache(); } catch (e) { console.log(e); } } }, { type: 'separator' }, { label: 'Inspect element', click: () => { browserWindow.webContents.inspectElement(x, y); }, accelerator: 'CommandOrControl+Shift+I' }); const currentMenu = Menu.buildFromTemplate(menuTemplate); currentMenu.popup(browserWindow, { async: true }); }; Window.defineDraggableArea = function () { }; Window.updateOptions = function (identity, updateObj) { let browserWindow = getElectronBrowserWindow(identity, 'update settings for'); try { for (var opt in updateObj) { if (optionSetters[opt]) { optionSetters[opt](updateObj[opt], browserWindow); } } } catch (e) { console.log(e.message); } }; Window.exists = function (identity) { return coreState.windowExists(identity.uuid, identity.name); }; Window.getBoundsFromDisk = function (identity, callback, errorCallback) { getBoundsCacheSafeFileName(identity, cacheFile => { try { fs.readFile(cacheFile, 'utf8', (err, data) => { if (err) { errorCallback(err); } else { try { callback(JSON.parse(data)); } catch (parseErr) { errorCallback(new Error(`Error parsing saved bounds data ${parseErr.message}`)); } } }); } catch (err) { errorCallback(err); } }, errorCallback); }; Window.authenticate = function (identity, username, password, callback) { let { authCallback } = authentication_delegate_1.getPendingAuthRequest(identity); if (authCallback && typeof (authCallback) === 'function') { authCallback(username, password); authentication_delegate_1.deletePendingAuthRequest(identity); callback(); } else { callback(new Error('No authentication request pending for window')); } }; Window.getZoomLevel = function (identity, callback) { let browserWindow = getElectronBrowserWindow(identity, 'get zoom level for'); browserWindow.webContents.getZoomLevel(callback); }; Window.setZoomLevel = function (identity, level) { let browserWindow = getElectronBrowserWindow(identity, 'set zoom level for'); browserWindow.webContents.send('zoom', { level }); }; Window.onUnload = (identity) => { of_events_1.default.emit(route_1.default.window('unload', identity.uuid, identity.name, false), identity); of_events_1.default.emit(route_1.default.window('init-subscription-listeners'), identity); }; Window.registerWindowName = (identity) => { coreState.registerPendingWindowName(identity.uuid, identity.name); }; function emitCloseEvents(identity) { const { uuid, name } = identity; of_events_1.default.emit(route_1.default.window('unload', uuid, name, false), identity); of_events_1.default.emit(route_1.default.window('openfin-diagnostic/unload', uuid, name, true), identity); electronApp.emit('browser-window-closed', null, getElectronBrowserWindow(identity)); of_events_1.default.emit(route_1.default.window('closed', uuid, name, true), { topic: 'window', type: 'closed', uuid, name }); of_events_1.default.emit(route_1.default.window('init-subscription-listeners'), identity); } function emitReloadedEvent(identity, url) { const { uuid, name } = identity; of_events_1.default.emit(route_1.default.window('reloaded', uuid, name), { uuid, name, url }); } function createWindowTearDown(identity, id, browserWindow, _boundsChangedHandler) { const promises = []; promises.push(new Promise(resolve => { browserWindow.once('closed', resolve); })); function closeChildWin(childId) { return new Promise((resolve, reject) => { const child = coreState.getWinObjById(childId); if (child) { let childIdentity = { name: child.name, uuid: child.uuid }; Window.close(childIdentity, true, () => { resolve(); }); } else { resolve(); } }); } function handleSaveStateAlwaysResolve() { return new Promise((resolve, reject) => { if (browserWindow._options.saveWindowState) { browserWindow.webContents.getZoomLevel(zoomLevel => { const cachedBounds = _boundsChangedHandler.getCachedBounds(); saveBoundsToDisk(identity, cachedBounds, zoomLevel, err => { if (err) { log.writeToLog('info', err); } _boundsChangedHandler.teardown(); resolve(); }); }); } else { _boundsChangedHandler.teardown(); resolve(); } }); } return function () { let ofWindow = Window.wrap(identity.uuid, identity.name); let childWindows = coreState.getChildrenByWinId(id) || []; coreState.removeChildById(id); promises.push(window_groups_1.default.leaveGroup(ofWindow)); promises.push(handleSaveStateAlwaysResolve()); childWindows.forEach(childId => { promises.push(closeChildWin(childId)); }); return Promise.all(promises).then(() => { emitCloseEvents(identity); browserWindow.removeAllListeners(); }); }; } function saveBoundsToDisk(identity, bounds, zoomLevel, callback) { getBoundsCacheSafeFileName(identity, cacheFile => { const data = { 'active': 'true', 'height': bounds.height, 'width': bounds.width, 'left': bounds.x, 'top': bounds.y, 'name': identity.name, 'windowState': bounds.windowState, 'zoomLevel': zoomLevel }; try { const userCache = electronApp.getPath('userCache'); fs.mkdir(path.join(userCache, windowPosCacheFolder), () => { fs.writeFile(cacheFile, JSON.stringify(data), (writeFileErr) => { callback(writeFileErr); }); }); } catch (err) { callback(err); } }, callback); } function getBoundsCacheSafeFileName(identity, callback, errorCallback) { const userCache = electronApp.getPath('userCache'); const hash = crypto.createHash('sha256'); hash.update(identity.uuid); hash.update(identity.name); const safeName = hash.digest('hex'); const newFileName = path.join(userCache, windowPosCacheFolder, `${safeName}.json`); try { fs.access(newFileName, fs.constants.F_OK, (newFileErr) => { if (newFileErr) { const oldSafeName = new Buffer(identity.uuid + '-' + identity.name).toString('hex'); const oldFileName = path.join(userCache, windowPosCacheFolder, `${oldSafeName}.json`); fs.access(oldFileName, fs.constants.F_OK, (oldFileErr) => { if (!oldFileErr) { fs.rename(oldFileName, newFileName, () => { callback(newFileName); }); } else { callback(newFileName); } }); } else { callback(newFileName); } }); } catch (err) { errorCallback(err); } } function applyAdditionalOptionsToWindowOnVisible(browserWindow, callback) { if (browserWindow.isVisible()) { callback(); } else { browserWindow.once('visibility-changed', (event, isVisible) => { if (isVisible) { if (browserWindow.isVisible()) { callback(); } else { setTimeout(() => { callback(); }, 1); } } }); } } function handleForceActions(identity, force, eventType, eventPayload, defaultAction) { let appEventString = route_1.default.application(`window-${eventType}`, identity.uuid); let winEventString = route_1.default.window(eventType, identity.uuid, identity.name); let listenerCount = of_events_1.default.listenerCount(winEventString); if (eventType === 'show-requested') { listenerCount += of_events_1.default.listenerCount(appEventString); } if (!listenerCount || force) { defaultAction(); } else { eventPayload.name = identity.name; eventPayload.uuid = identity.uuid; eventPayload.type = eventType; eventPayload.topic = 'window'; of_events_1.default.emit(winEventString, eventPayload); } } function applyAdditionalOptionsToWindow(browserWindow) { let options = browserWindow && browserWindow._options; if (!options) { return; } browserWindow.setTaskbarGroup(options.taskbarIconGroup); if (!options.frame) { browserWindow.setRoundedCorners(options.cornerRounding.width, options.cornerRounding.height); } applyAdditionalOptionsToWindowOnVisible(browserWindow, () => { if (!browserWindow.isDestroyed()) { if (options.alphaMask.red > -1 && options.alphaMask.green > -1 && options.alphaMask.blue > -1) { browserWindow.setAlphaMask(options.alphaMask.red, options.alphaMask.green, options.alphaMask.blue); } else if (options.opacity < 1) { browserWindow.setOpacity(options.opacity); } if (options.aspectRatio > 0) { browserWindow.setAspectRatio(options.aspectRatio); } if (options.state === 'minimized') { browserWindow.minimize(); } else if (options.state === 'maximized') { browserWindow.maximize(); } if (!options.frame) { browserWindow.setResizeRegion(options.resizeRegion.size); browserWindow.setResizeRegionBottomRight(options.resizeRegion.bottomRightCorner); } browserWindow.setResizeSides(options.resizeRegion.sides.top, options.resizeRegion.sides.right, options.resizeRegion.sides.bottom, options.resizeRegion.sides.left); } }); } function getOptFromBrowserWin(opt, browserWin, defaultVal) { var opts = browserWin && browserWin._options, optVal = opts && opts[opt]; if (optVal === undefined) { return defaultVal; } return optVal; } function setOptOnBrowserWin(opt, val, browserWin) { var opts = browserWin && browserWin._options; if (opts) { opts[opt] = val; } } function closeRequestedDecorator(payload) { let propagate = true; payload.force = false; return propagate; } function boundsChangeDecorator(payload, args) { let boundsChangePayload = args[0]; let payloadIsObject = typeof boundsChangePayload === 'object'; let requiredKeys = ['top', 'left', 'reason', 'width', 'height']; let commonKeys = _.intersection(_.keys(boundsChangePayload), requiredKeys); let allRequiredKeysPresent = commonKeys.length === requiredKeys.length; let shouldExtendPayload = payloadIsObject && allRequiredKeysPresent; if (shouldExtendPayload) { Object.keys(boundsChangePayload).forEach(function (key) { payload[key] = boundsChangePayload[key]; }); let _win = Window.wrap(payload.uuid, payload.name); let _browserWin = _win && _win.browserWindow; setOptOnBrowserWin('x', payload.left, _browserWin); setOptOnBrowserWin('y', payload.top, _browserWin); setOptOnBrowserWin('width', payload.width, _browserWin); setOptOnBrowserWin('height', payload.height, _browserWin); return true; } else { return false; } } function disabledFrameBoundsChangeDecorator(payload, args) { var propogate = false; if (args.length >= 3) { var bounds = args[1]; var type = args[2]; payload.changeType = type; payload.left = bounds.x; payload.top = bounds.y; payload.width = bounds.width; payload.height = bounds.height; payload.deferred = false; propogate = true; } return propogate; } function opacityChangedDecorator(payload, args) { let _win = Window.wrap(payload.uuid, payload.name); let _browserWin = _win && _win.browserWindow; setOptOnBrowserWin('opacity', args[1], _browserWin); return false; } function visibilityChangedDecorator(payload, args) { let propogate = false; if (args.length >= 2) { const [, visible, closing] = args; if (visible) { payload.type = 'shown'; let uuid = payload.uuid; if (uuid && !coreState.sentFirstHideSplashScreen(uuid)) { let emitHideSplashScreen = require('./application.js').Application.emitHideSplashScreen; emitHideSplashScreen({ uuid }); coreState.setSentFirstHideSplashScreen(uuid, true); } } else { let openfinWindow = Window.wrap(payload.uuid, payload.name); const { hideReason } = openfinWindow; payload.type = 'hidden'; payload.reason = hideReason === 'hide' && closing ? 'closing' : hideReason; openfinWindow.hideReason = 'hide'; } propogate = true; } return propogate; } function responseReceivedDecorator(payload, args) { var [, status, newUrl, originalUrl, httpResponseCode, requestMethod, referrer, headers, resourceType] = args; Object.assign(payload, { status, newUrl, originalUrl, httpResponseCode, requestMethod, referrer, headers, resourceType }); return true; } function loadFailedDecorator(payload, args) { var [, errorCode, errorDescription, validatedURL, isMainFrame] = args; Object.assign(payload, { errorCode, errorDescription, validatedURL, isMainFrame }); return true; } function noOpDecorator() { return true; } function calcBoundsAnchor(anchor, newWidth, newHeight, currBounds) { let calcAnchor = { x: currBounds.x, y: currBounds.y }; if (!anchor) { return calcAnchor; } let anchors = anchor.split('-'); let yAnchor = anchors[0]; let xAnchor = anchors[1]; if (yAnchor === 'bottom' && currBounds.height !== newHeight) { calcAnchor.y = currBounds.y + (currBounds.height - newHeight); } if (xAnchor === 'right' && currBounds.width !== newWidth) { calcAnchor.x = currBounds.x + (currBounds.width - newWidth); } return calcAnchor; } function setTaskbar(browserWindow, forceFetch = false) { const options = browserWindow._options; setBlankTaskbarIcon(browserWindow); if (!main_1.isHttpUrl(options.url)) { let _url = getWinOptsIconUrl(options); if (!main_1.isHttpUrl(_url) && !main_1.isFileUrl(_url)) { _url = 'file:///' + _url; } setTaskbarIcon(browserWindow, _url, () => { if (!browserWindow.isDestroyed()) { setTaskbarIcon(browserWindow, getMainWinIconUrl(browserWindow.id)); } }); return; } browserWindow.webContents.on('page-favicon-updated', (event, urls) => { setTaskbarIcon(browserWindow, getWinOptsIconUrl(options), () => { if (!browserWindow.isDestroyed()) { const _url = urls && urls[0]; setTaskbarIcon(browserWindow, _url, () => { if (!browserWindow.isDestroyed()) { setTaskbarIcon(browserWindow, getMainWinIconUrl(browserWindow.id)); } }); } }); }); if (forceFetch) { setTaskbarIcon(browserWindow, getWinOptsIconUrl(options), () => { if (!browserWindow.isDestroyed()) { setTaskbarIcon(browserWindow, getMainWinIconUrl(browserWindow.id)); } }); } } function setTaskbarIcon(browserWindow, iconUrl, errorCallback = () => { }) { const identity = main_1.getIdentityFromObject(browserWindow._options); cached_resource_fetcher_1.cachedFetch(identity, iconUrl, (error, iconFilepath) => { if (!error) { setIcon(browserWindow, iconFilepath, errorCallback); } else { errorCallback(); } }); } function setIcon(browserWindow, iconFilepath, errorCallback = () => { }) { if (!browserWindow.isDestroyed()) { let icon = nativeImage.createFromPath(iconFilepath); if (icon.isEmpty()) { errorCallback(); } else { browserWindow.setIcon(icon); } } } function setBlankTaskbarIcon(browserWindow) { setIcon(browserWindow, path.resolve(`${__dirname}/../../../assets/blank.ico`)); } function getMainWinIconUrl(id) { let options = coreState.getMainWindowOptions(id) || {}; return getWinOptsIconUrl(options); } function getWinOptsIconUrl(options) { return options.icon || options.taskbarIcon || options.applicationIcon; } function handleCustomAlerts(id, opts) { let browserWindow = BrowserWindow.fromId(id); let subTopic = 'alert'; let type = 'window-alert-requested'; let topic = 'application'; function subscription(e, args) { let message = args[0][0]; let payload = { uuid: opts.uuid, name: opts.name, message: message, url: browserWindow.webContents.getURL(), topic: topic, type: type }; if (typeof (e.preventDefault) === 'function') { e.preventDefault(); } of_events_1.default.emit(route_1.default(topic, type, opts.uuid), payload); } function unsubscribe() { if (browserWindow) { browserWindow.removeListener(subTopic, subscription); } } browserWindow.on(subTopic, subscription); subscriptionManager.registerSubscription(unsubscribe, { uuid: opts.uuid, name: opts.name }, type, id); } function getElectronBrowserWindow(identity, errDesc) { let openfinWindow = Window.wrap(identity.uuid, identity.name); let browserWindow = openfinWindow && openfinWindow.browserWindow; if (errDesc && !browserWindow) { throw new Error(`Could not ${errDesc} unknown window named '${identity.name}'`); } return browserWindow; } function restoreWindowPosition(identity, cb) { Window.getBoundsFromDisk(identity, savedBounds => { const monitorInfo = system_1.System.getMonitorInfo(); if (!boundsVisible(savedBounds, monitorInfo)) { const displayRoot = system_1.System.getNearestDisplayRoot({ x: savedBounds.left, y: savedBounds.top }); savedBounds.top = displayRoot.y; savedBounds.left = displayRoot.x; } Window.setBounds(identity, savedBounds.left, savedBounds.top, savedBounds.width, savedBounds.height); switch (savedBounds.windowState) { case 'maximized': Window.maximize(identity); break; case 'minimized': Window.minimize(identity); break; } const { zoomLevel } = savedBounds; Window.setZoomLevel(identity, zoomLevel); cb(); }, (err) => { log.writeToLog('info', err); cb(); }); } function intersectsRect(bounds, rect) { return !(bounds.left > rect.right || (bounds.left + bounds.width) < rect.left || bounds.top > rect.bottom || (bounds.top + bounds.height) < rect.top); } function boundsVisible(bounds, monitorInfo) { let visible = false; const monitors = [monitorInfo.primaryMonitor].concat(monitorInfo.nonPrimaryMonitors); for (let i = 0; i < monitors.length; i++) { if (intersectsRect(bounds, monitors[i].monitorRect)) { visible = true; } } return visible; } module.exports.Window = Window; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const pendingAuthRequests = new Map(); function generateId(identity) { const { uuid, name } = identity; return `${uuid}-${name}`; } function addPendingAuthRequests(identity, authInfo, authCallback) { const id = generateId(identity); pendingAuthRequests.set(id, { authCallback, authInfo, identity }); } exports.addPendingAuthRequests = addPendingAuthRequests; function getPendingAuthRequest(identity) { const id = generateId(identity); return pendingAuthRequests.get(id); } exports.getPendingAuthRequest = getPendingAuthRequest; function deletePendingAuthRequest(identity) { const id = generateId(identity); pendingAuthRequests.delete(id); } exports.deletePendingAuthRequest = deletePendingAuthRequest; function createAuthUI(identity) { const Application = require('./api/application.js').Application; const appId = `auth-app-${electron_1.app.generateGUID()}`; const uriUuid = encodeURIComponent(identity.uuid); const uriName = encodeURIComponent(identity.name); const resourceFetch = identity.resourceFetch || false; Application.create({ url: `file:///${__dirname}/../../assets/login.html?uuid=${uriUuid}&name=${uriName}&resourceFetch=${resourceFetch}`, uuid: appId, name: appId, mainWindowOptions: { alwaysOnTop: true, autoShow: true, defaultCentered: true, defaultHeight: 271, defaultTop: true, defaultWidth: 362, frame: false, resizable: false }, _runtimeAuthDialog: true }); Application.run({ uuid: appId }); } exports.createAuthUI = createAuthUI; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const WindowTransaction = require('electron').windowTransaction; const _ = require("underscore"); const animations = require("./animations.js"); const coreState = require("./core_state.js"); const Deferred = require("./deferred"); const window_groups_1 = require("./window_groups"); const window_group_transaction_tracker_1 = require("./window_group_transaction_tracker"); const safe_int_1 = require("../common/safe_int"); const of_events_1 = require("./of_events"); const route_1 = require("../common/route"); const utils_1 = require("./utils"); const rectangle_1 = require("./rectangle"); const log_1 = require("./log"); const POSITION = 0; const SIZE = 1; const POSITION_AND_SIZE = 2; const isWin32 = process.platform === 'win32'; const DisableWindowGroupTracking = 'disable-window-group-tracking'; const shouldTrack = (action) => { return !coreState.argo[DisableWindowGroupTracking] || !coreState.argo[DisableWindowGroupTracking].split(',').includes(action); }; const trackingResize = shouldTrack('resize'); const trackingAPI = shouldTrack('api'); var WindowState; (function (WindowState) { WindowState["Normal"] = "normal"; WindowState["Maximized"] = "maximized"; WindowState["Minimized"] = "minimized"; })(WindowState || (WindowState = {})); class BoundsChangedStateTracker { constructor(uuid, name, browserWindow) { this.uuid = uuid; this.name = name; this.browserWindow = browserWindow; this.sizeChanged = false; this.positionChanged = false; this._userBoundsChangeActive = false; this._deferred = false; this._deferredEvents = []; this.setUserBoundsChangeActive = (enabled) => { this._userBoundsChangeActive = enabled; }; this.isUserBoundsChangeActive = () => { return this._userBoundsChangeActive; }; this.updateCachedBounds = (bounds) => { this._cachedBounds = bounds; }; this.getCachedBounds = () => { return this._cachedBounds; }; this.getCurrentBounds = () => { const bounds = this.browserWindow.getBounds(); let windowState = WindowState.Normal; if (this.browserWindow.isMaximized()) { windowState = WindowState.Maximized; } if (this.browserWindow.isMinimized()) { windowState = WindowState.Minimized; } const frame = this.browserWindow._options.frame; return Object.assign({}, bounds, { frame, windowState }); }; this.compareBoundsResult = (boundsOne, boundsTwo) => { let xDiff = boundsOne.x !== boundsTwo.x; let yDiff = boundsOne.y !== boundsTwo.y; const widthDiff = boundsOne.width !== boundsTwo.width; const heightDiff = boundsOne.height !== boundsTwo.height; const stateDiff = boundsOne.windowState !== boundsTwo.windowState; const changed = xDiff || yDiff || widthDiff || heightDiff; this.sizeChanged = this.sizeChanged || (widthDiff || heightDiff); if (this.sizeChanged) { xDiff = xDiff && ((boundsOne.x - boundsTwo.x) !== (boundsTwo.width - boundsOne.width)); yDiff = yDiff && ((boundsOne.y - boundsTwo.y) !== (boundsTwo.height - boundsOne.height)); } this.positionChanged = this.positionChanged || (xDiff || yDiff); return { x: xDiff, y: yDiff, width: widthDiff, height: heightDiff, state: stateDiff, changed }; }; this.getBoundsDelta = (current, cached) => { return { x: current.x - cached.x, x2: (current.x + current.width) - (cached.x + cached.width), y: current.y - cached.y, y2: (current.y + current.height) - (cached.y + cached.height), width: current.width - cached.width, height: current.height - cached.height }; }; this.boundsChangeReason = (name, groupUuid) => { if (groupUuid) { const groupLeader = window_group_transaction_tracker_1.default.getGroupLeader(groupUuid); if (groupLeader && groupLeader.uuid && groupLeader.name) { const ofWindow = coreState.getWindowByUuidName(groupLeader.uuid, groupLeader.name); if (ofWindow && animations.getAnimationHandler().hasWindow(ofWindow.browserWindow.id)) { return groupLeader.name === name ? 'animation' : 'group-animation'; } else { return groupLeader.name === name ? 'self' : 'group'; } } } return animations.getAnimationHandler().hasWindow(this.browserWindow.id) ? 'animation' : 'self'; }; this.sharedBoundPixelDiff = 5; this.sharedBound = (boundOne, boundTwo) => { return Math.abs(boundOne - boundTwo) < this.sharedBoundPixelDiff; }; this.handleGroupedResize = (windowToUpdate, bounds) => { if (!trackingResize) { return bounds; } const thisRect = rectangle_1.Rectangle.CREATE_FROM_BOUNDS(bounds); const currentBounds = this.getCurrentBounds(); const cachedBounds = this.getCachedBounds(); const moved = thisRect.move(cachedBounds, currentBounds); return utils_1.clipBounds(moved, windowToUpdate.browserWindow); }; this.checkTrackingApi = (groupLeader) => groupLeader.type === 'api' ? !!trackingAPI : true; this.handleBoundsChange = (isAdditionalChangeExpected, force = false, bounds = null) => { let dispatchedChange = false; let currentBounds = this.getCurrentBounds(); if (bounds) { currentBounds = { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height, frame: currentBounds.frame, windowState: currentBounds.windowState }; } const cachedBounds = this.getCachedBounds(); const boundsCompare = this.compareBoundsResult(currentBounds, cachedBounds); const stateMin = boundsCompare.state && currentBounds.windowState === 'minimized'; const eventType = isAdditionalChangeExpected ? 'bounds-changing' : 'bounds-changed'; const sizeChangedCriteria = [ boundsCompare.width, boundsCompare.height ]; const positionChangedCriteria = [ boundsCompare.x, boundsCompare.y ]; const isBoundsChanged = eventType === 'bounds-changed'; if (isBoundsChanged) { log_1.writeToLog(1, `JavaLog handleBoundsChange ${isAdditionalChangeExpected} ${JSON.stringify(currentBounds)} `, true); } if (isBoundsChanged) { sizeChangedCriteria.push(this.sizeChanged); positionChangedCriteria.push(this.positionChanged); } if (boundsCompare.changed && !stateMin || force) { const sizeChange = _.some(sizeChangedCriteria, (criteria) => { return criteria; }); const posChange = _.some(positionChangedCriteria, (criteria) => { return criteria; }); const changeType = (sizeChange ? (posChange ? POSITION_AND_SIZE : SIZE) : POSITION); const ofWindow = coreState.getWindowByUuidName(this.uuid, this.name); const groupUuid = ofWindow ? ofWindow.groupUuid : null; const reason = this.boundsChangeReason(this.name, groupUuid); if (groupUuid && coreState.argo['use-legacy-window-groups']) { let groupLeader = window_group_transaction_tracker_1.default.getGroupLeader(groupUuid); if (force) { if (groupLeader && groupLeader.name === this.name) { if (groupLeader.type !== 'api') { window_group_transaction_tracker_1.default.notifyEndTransaction(groupUuid); } window_group_transaction_tracker_1.default.clearGroup(groupUuid); } } else { if (!groupLeader || !groupLeader.name) { const type = this.isUserBoundsChangeActive() ? 'user' : animations.getAnimationHandler().hasWindow(this.browserWindow.id) ? 'animation' : 'api'; window_group_transaction_tracker_1.default.setGroupLeader(groupUuid, this.name, this.uuid, type); } } groupLeader = window_group_transaction_tracker_1.default.getGroupLeader(groupUuid); if (groupLeader && groupLeader.name === this.name && this.checkTrackingApi(groupLeader)) { const delta = this.getBoundsDelta(currentBounds, cachedBounds); let wt; const hwndToId = {}; const { flag: { noZorder, noSize, noActivate } } = WindowTransaction; let flags; if (changeType === SIZE) { flags = noZorder + noActivate; } else { flags = noZorder + noSize + noActivate; } const windowGroup = window_groups_1.default.getGroup(groupUuid); const winsToMove = []; const positions = new Map(); const setPositions = []; for (let i = 0; i < windowGroup.length; i++) { if (windowGroup[i].name !== this.name) { winsToMove.push(windowGroup[i]); } positions.set(windowGroup[i].name, windowGroup[i].browserWindow.getBounds()); } for (let i = 0; i < winsToMove.length; i++) { const win = winsToMove[i]; win.browserWindow.bringToFront(); const winBounds = positions.get(win.name); const bounds = (changeType === SIZE) ? this.handleGroupedResize(win, winBounds) : winBounds; let { x, y } = bounds; const { width, height } = bounds; if (changeType === POSITION || changeType === POSITION_AND_SIZE) { x = safe_int_1.toSafeInt(x + delta.x, x); y = safe_int_1.toSafeInt(y + delta.y, y); } if (isWin32) { const hwnd = parseInt(win.browserWindow.nativeId, 16); if (!wt) { wt = new WindowTransaction.Transaction(0); wt.on('deferred-set-window-pos', (event, payload) => { payload.forEach((winPos) => { const bwId = hwndToId[parseInt(winPos.hwnd)]; Deferred.handleMove(bwId, winPos); }); }); } hwndToId[hwnd] = win.browserWindow.id; if (win.browserWindow.isMaximized()) { win.browserWindow.unmaximize(); } const [w, h] = [width, height]; wt.setWindowPos(hwnd, { x, y, w, h, flags }); } else { if (win.browserWindow.isFullScreen()) { win.browserWindow.setFullScreen(false); } else if (win.browserWindow.isMaximized()) { win.browserWindow.unmaximize(); } else { positions.set(win.name, { x, y, width, height }); win.browserWindow.setBounds(positions.get(win.name)); } } } if (wt) { wt.commit(); } } } const payload = { changeType, reason, name: this.name, uuid: this.uuid, type: eventType, deferred: this._deferred, top: currentBounds.y, left: currentBounds.x, height: currentBounds.height, width: currentBounds.width }; if (this._deferred) { this._deferredEvents.push(payload); } else { this.browserWindow.emit('synth-bounds-change', payload); } dispatchedChange = true; } this.updateCachedBounds(currentBounds); if (!isAdditionalChangeExpected) { this.sizeChanged = false; this.positionChanged = false; } return dispatchedChange; }; this.collapseEventReasonTypes = (eventsList) => { const eventGroups = []; eventsList.forEach((event, index) => { if (index === 0 || event.reason !== eventsList[index - 1].reason) { const list = []; list.push(event); eventGroups.push(list); } else { _.last(eventGroups).push(event); } }); return eventGroups.map((group) => { let sizeChange = false; let posChange = false; group.forEach((event) => { if (event.changeType === POSITION) { posChange = true; } else if (event.changeType === SIZE) { sizeChange = true; } else { sizeChange = true; posChange = true; } }); const lastEvent = _.last(group); lastEvent.changeType = (sizeChange ? (posChange ? POSITION_AND_SIZE : SIZE) : POSITION); return lastEvent; }); }; this.dispatchDeferredEvents = () => { const boundsChangedEvents = this._deferredEvents.filter((event) => { return event.type === 'bounds-changed'; }); const reasonGroupedEvents = this.collapseEventReasonTypes(boundsChangedEvents); reasonGroupedEvents.forEach((event) => { event.type = 'bounds-changing'; this.browserWindow.emit('synth-bounds-change', event); event.type = 'bounds-changed'; this.browserWindow.emit('synth-bounds-change', event); }); this._deferredEvents.length = 0; }; this.endWindowGroupTransactionListener = (groupUuid) => { const ofWindow = coreState.getWindowByUuidName(this.uuid, this.name); const _groupUuid = ofWindow ? ofWindow.groupUuid : null; if (_groupUuid === groupUuid) { const groupLeader = window_group_transaction_tracker_1.default.getGroupLeader(groupUuid); if (groupLeader && groupLeader.name !== this.name) { this.handleBoundsChange(false, true); } } }; this.updateEvents = (register) => { const listenerFn = register ? 'on' : 'removeListener'; Object.keys(this._listeners).forEach((key) => { this.browserWindow[listenerFn](key, this._listeners[key]); }); window_group_transaction_tracker_1.default[listenerFn]('end-window-group-transaction', this.endWindowGroupTransactionListener); }; this.hookListeners = () => { this.updateEvents(true); }; this.unHookListeners = () => { this.updateEvents(false); }; this.teardown = () => { this.unHookListeners(); }; this._listeners = { 'begin-user-bounds-change': () => { this.setUserBoundsChangeActive(true); const cachedBounds = this.getCachedBounds(); const payload = { uuid, name, top: cachedBounds.y, left: cachedBounds.x }; of_events_1.default.emit(route_1.default.window('begin-user-bounds-changing', uuid, name), Object.assign(payload, cachedBounds)); }, 'end-user-bounds-change': () => { this.setUserBoundsChangeActive(false); const bounds = this.getCurrentBounds(); const payload = { uuid, name, top: bounds.y, left: bounds.x }; of_events_1.default.emit(route_1.default.window('end-user-bounds-changing', uuid, name), Object.assign(payload, bounds)); this.handleBoundsChange(false, true); }, 'bounds-changing': (event, bounds) => { this.handleBoundsChange(true, false, bounds); }, 'bounds-changed': () => { const ofWindow = coreState.getWindowByUuidName(uuid, name); const groupUuid = ofWindow ? ofWindow.groupUuid : null; const dispatchedChange = this.handleBoundsChange(true); if (groupUuid && !coreState.argo['use-legacy-window-groups']) { return; } if (dispatchedChange) { if (groupUuid) { const groupLeader = window_group_transaction_tracker_1.default.getGroupLeader(groupUuid); if (groupLeader && groupLeader.type === 'api') { this.handleBoundsChange(false, true); } } else { if (!animations.getAnimationHandler().hasWindow(this.browserWindow.id) && !this.isUserBoundsChangeActive()) { this.handleBoundsChange(false, true); } } } }, 'synth-animate-end': (meta) => { if (meta.bounds) { this.handleBoundsChange(false, true); } }, 'visibility-changed': (event, isVisible) => { if (!isVisible || this.browserWindow.isMinimized() || this.browserWindow.isMaximized()) { this._deferred = true; } else { this._deferred = false; this.dispatchDeferredEvents(); } }, 'minimize': () => { this._deferred = true; this.updateCachedBounds(this.getCurrentBounds()); }, 'maximize': () => { this._deferred = true; this.updateCachedBounds(this.getCurrentBounds()); }, 'restore': () => { this._deferred = false; utils_1.windowSetBoundsToVisible(this.browserWindow); this.updateCachedBounds(this.getCurrentBounds()); this.dispatchDeferredEvents(); }, 'unmaximize': () => { this._deferred = false; this.updateCachedBounds(this.getCurrentBounds()); this.dispatchDeferredEvents(); }, 'deferred-set-bounds': (event, payload) => { Deferred.handleMove(this.browserWindow.id, payload); } }; this.updateCachedBounds(this.getCurrentBounds()); this.hookListeners(); } } exports.default = BoundsChangedStateTracker; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const fs_1 = require("fs"); const path_1 = require("path"); const url_1 = require("url"); const crypto_1 = require("crypto"); const log = require("./log"); const main_1 = require("../common/main"); const authentication_delegate_1 = require("./authentication_delegate"); const core_state_1 = require("./core_state"); let appQuiting = false; let cacheCleared = false; const expectedStatusCode = /^[23]/; const fetchMap = new Map(); const authMap = new Map(); electron_1.app.on('quit', () => { appQuiting = true; }); async function cachedFetch(identity, url, callback) { if (typeof url !== 'string') { callback(new Error(`Bad file url: '${url}'`)); return; } if (appQuiting) { callback(new Error('Runtime is exiting')); return; } if (!main_1.isHttpUrl(url)) { if (main_1.isFileUrl(url)) { callback(null, main_1.uriToPath(url)); } else { fs_1.stat(url, (err) => { if (err) { electron_1.app.vlog(1, `cachedFetch invalid file url ${url}`); callback(new Error(`Invalid file url: '${url}'`)); } else { callback(null, url); } }); } return; } const { uuid } = identity; const appCacheDir = getAppCacheDir(uuid); const filePath = getFilePath(appCacheDir, url); let err; electron_1.app.vlog(1, `cachedFetch ${url} ${filePath}`); if (fetchMap.has(filePath)) { fetchMap.get(filePath).then(() => callback(null, filePath)).catch(callback); } else { const p = new Promise(async (resolve, reject) => { try { await prepDownloadLocation(appCacheDir); await download(identity, url, filePath, appCacheDir); callback(null, filePath); resolve(filePath); } catch (e) { err = e; electron_1.app.vlog(1, `cachedFetch uuid ${uuid} url ${url} file ${filePath} err ${e.message}`); callback(err, filePath); reject(err); } finally { fetchMap.delete(filePath); } }); fetchMap.set(filePath, p); } } exports.cachedFetch = cachedFetch; function pathExists(location) { return new Promise((resolve, reject) => { fs_1.stat(location, (err) => { if (err) { resolve(false); } else { resolve(true); } }); }); } function makeDirectory(location) { return new Promise((resolve, reject) => { fs_1.mkdir(location, (err) => { if (err) { electron_1.app.vlog(1, `cachedFetch makeDirectory error, check EEXIST ${err.message}`); pathExists(location).then(value => value ? resolve() : reject(err)) .catch(() => { electron_1.app.vlog(1, `cachedFetch makeDirectory error ${err.message}`); reject(err); }); } else { resolve(); } }); }); } async function prepDownloadLocation(appCacheDir) { const appCacheDirExists = await pathExists(appCacheDir); if (appCacheDirExists) { return; } const rootCachePath = getRootCachePath(); const cacheRootPathExists = await pathExists(rootCachePath); if (!cacheRootPathExists) { await makeDirectory(rootCachePath); } const cacheAppPathExists = await pathExists(appCacheDir); if (!cacheAppPathExists) { await makeDirectory(appCacheDir); } return; } function getRootCachePath() { const p = process; if (p.buildFlags.enableChromium) { return path_1.join(electron_1.app.getPath('userData'), 'Default', 'Cache'); } else { return path_1.join(electron_1.app.getPath('userData'), 'Cache'); } } function getAppCacheDir(appUuid) { const appUuidHash = generateHash(appUuid); const rootCachePath = getRootCachePath(); return path_1.join(rootCachePath, appUuidHash); } function getFilePath(appCacheDir, fileUrl) { const fileUrlHash = generateHash(fileUrl); const fileUrlPathname = url_1.parse(fileUrl).pathname; const fileExt = path_1.parse(fileUrlPathname).ext; const filename = fileUrlHash + fileExt; return path_1.join(appCacheDir, filename); } function generateHash(str) { const hash = crypto_1.createHash('sha256'); hash.update(str); return hash.digest('hex'); } function authRequest(url, authInfo, authCallback) { const uuid = electron_1.app.generateGUID(); const identity = { name: uuid, uuid: uuid, resourceFetch: true }; log.writeToLog(1, `fetchURL login event ${url} uuid ${uuid} ${JSON.stringify(authInfo)}`, true); authMap.set(uuid, authCallback); authentication_delegate_1.addPendingAuthRequests(identity, authInfo, authCallback); authentication_delegate_1.createAuthUI(identity); } async function download(identity, url, saveToPath, appCacheDir) { return new Promise(async (resolve, reject) => { const session = core_state_1.getSession(identity); const request = electron_1.net.request(url); if (cacheCleared) { electron_1.app.vlog(1, 'prepare download location again after clear cache'); await prepDownloadLocation(appCacheDir); } const binaryWriteStream = fs_1.createWriteStream(saveToPath, { encoding: 'binary' }); request.once('response', (response) => { const { statusCode } = response; electron_1.app.vlog(1, `cachedFetch download status ${saveToPath} ${statusCode} `); response.setEncoding('binary'); response.on('data', (chunk) => { binaryWriteStream.write(chunk, 'binary'); }); response.once('error', (err) => { reject(err); }); response.on('end', () => { binaryWriteStream.once('close', () => { resolve(); }); binaryWriteStream.once('error', (err) => { reject(err); }); binaryWriteStream.end(); if (!expectedStatusCode.test(statusCode)) { const error = new Error(`Failed to download resource. Status code: ${statusCode}`); reject(error); } }); }); request.on('login', (authInfo, callback) => { authRequest(saveToPath, authInfo, callback); }); request.once('error', (err) => { reject(err); }); if (session) { session.cookies.get({}, (error, cookies) => { if (error) { log.writeToLog(1, 'Error getting session cookies for identity ' + `${identity.uuid}-${identity.name} while trying to fetch a ` + `resource from URL ${url}. Will attempt to fetch the resource ` + `without cookies. Error received: ${error}`, true); } else { const cookiesNameValue = cookies.map((e) => `${e.name}=${e.value}`); request.setHeader('cookie', cookiesNameValue); } request.end(); }); } else { request.end(); } }); } function fetchURL(url, done, onError) { log.writeToLog(1, `fetchURL ${url}`, true); const request = electron_1.net.request(url); request.once('response', (response) => { let data = ''; const { statusCode } = response; log.writeToLog(1, `fetchURL statusCode: ${statusCode} for ${url}`, true); if (!expectedStatusCode.test(statusCode)) { const error = new Error(`Failed to download resource. Status code: ${statusCode}`); onError(error); } response.setEncoding('utf8'); response.once('error', (err) => { onError(err); }); response.on('data', (chunk) => { data = data.concat(chunk); }); response.on('end', () => { log.writeToLog(1, `Contents from ${url}`, true); log.writeToLog(1, data, true); try { const obj = JSON.parse(data); done(obj); } catch (e) { onError(new Error(`Error parsing JSON from ${url}`)); } }); }); request.on('login', (authInfo, callback) => { authRequest(url, authInfo, callback); }); request.once('error', (err) => { onError(err); }); request.end(); } exports.fetchURL = fetchURL; function fetchReadFile(url, isJSON) { return new Promise((resolve, reject) => { if (main_1.isHttpUrl(url)) { fetchURL(url, resolve, reject); } else if (main_1.isFileUrl(url)) { const pathToFile = main_1.uriToPath(url); readFile(pathToFile, isJSON) .then(resolve) .catch(reject); } else { fs_1.stat(url, (err) => { if (err) { reject(new Error(`URL protocol is not supported in ${url}`)); } else { readFile(url, isJSON) .then(resolve) .catch(reject); } }); } }); } exports.fetchReadFile = fetchReadFile; function readFile(pathToFile, isJSON) { return new Promise((resolve, reject) => { fs_1.readFile(pathToFile, 'utf-8', (error, data) => { if (error) { reject(error); } else { isJSON ? resolve(JSON.parse(data)) : resolve(data); } }); }); } exports.readFile = readFile; function authenticateFetch(uuid, username, password) { if (authMap.has(uuid)) { log.writeToLog(1, `Auth resource fetch uuid ${uuid} ${username}`, true); authMap.get(uuid).call(null, username, password); authMap.delete(uuid); } else { log.writeToLog(1, `Missing resource auth uuid ${uuid}`, true); } } exports.authenticateFetch = authenticateFetch; function clearCacheInvoked(cleared) { cacheCleared = cleared; } exports.clearCacheInvoked = clearCacheInvoked; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = clipBounds; function clipBounds(bounds, browserWindow) { if (!('_options' in browserWindow)) { return bounds; } const { minWidth, minHeight, maxWidth, maxHeight } = browserWindow._options; return { x: bounds.x, y: bounds.y, width: clamp(bounds.width, minWidth, maxWidth), height: clamp(bounds.height, minHeight, maxHeight) }; } function clamp(num, min = 0, max = Number.MAX_SAFE_INTEGER) { max = max < 0 ? Number.MAX_SAFE_INTEGER : max; return Math.min(Math.max(num, min, 0), max); } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const log = require("./log"); const coreState = require("./core_state"); const peer_connection_manager_1 = require("./runtime_p2p/peer_connection_manager"); const enableMeshFlag = 'enable-mesh'; const securityRealmFlag = 'security-realm'; let connectionManager; let meshEnabled = false; exports.meshEnabled = meshEnabled; buildNoopConnectionManager(); function startConnectionManager() { try { connectionManager = new peer_connection_manager_1.PeerConnectionManager(); exports.meshEnabled = meshEnabled = true; log.writeToLog('info', 'multi-runtime mode enabled'); } catch (e) { log.writeToLog('info', e.message); } } function buildNoopConnectionManager() { connectionManager = new events_1.EventEmitter(); connectionManager.connectToRuntime = () => { return new Promise((resolve, reject) => { reject(); }); }; connectionManager.resolveIdentity = () => { return new Promise((resolve, reject) => { reject(); }); }; connectionManager.connections = []; } function isMeshEnabled(args = {}) { let enabled = false; const enableMesh = args[enableMeshFlag]; const securityRealm = args[securityRealmFlag]; if (!securityRealm || enableMesh) { enabled = true; } return enabled; } exports.isMeshEnabled = isMeshEnabled; function isMeshEnabledRuntime(portInfo) { return portInfo.multiRuntime === true; } exports.isMeshEnabledRuntime = isMeshEnabledRuntime; function keyFromPortInfo(portInfo) { const { version, port, securityRealm = '' } = portInfo; return `${version}/${port}/${securityRealm}`; } exports.keyFromPortInfo = keyFromPortInfo; if (isMeshEnabled(coreState.argo)) { startConnectionManager(); } function getMeshUuid() { const portInfo = coreState.getSocketServerState(); return keyFromPortInfo(portInfo); } exports.getMeshUuid = getMeshUuid; exports.default = connectionManager; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); let fs = require('fs'); let path = require('path'); let queryString = require('querystring'); let _ = require('underscore'); let coreState = require('./core_state.js'); let log = require('./log'); const main_1 = require("../common/main"); const cached_resource_fetcher_1 = require("./cached_resource_fetcher"); const shapes_1 = require("../shapes"); const TRANSPARENT_WHITE = '#0FFF'; const contextMenuSettings = { 'enable': true, 'devtools': true, 'reload': true, }; const iframeBaseSettings = { 'crossOriginInjection': false, 'sameOriginInjection': true, 'enableDeprecatedSharedName': false }; const rendererBatchingBaseSettings = { 'enabled': false, 'maxSize': Number.MAX_VALUE, 'ttl': 0 }; function five0BaseOptions() { return { 'accelerator': { 'devtools': false, 'zoom': false, 'reload': false, 'reloadIgnoringCache': false }, 'alphaMask': { 'blue': -1, 'green': -1, 'red': -1 }, 'alwaysOnBottom': false, 'alwaysOnTop': false, 'api': { 'iframe': iframeBaseSettings }, 'applicationIcon': '', 'aspectRatio': 0, 'autoShow': false, 'backgroundThrottling': false, 'contextMenuSettings': contextMenuSettings, 'cornerRounding': { 'height': 0, 'width': 0 }, 'defaultCentered': false, 'defaultHeight': 500, 'defaultLeft': 10, 'defaultTop': 10, 'defaultWidth': 800, 'delay_connection': false, 'disableIabSecureLogging': false, 'draggable': false, 'exitOnClose': false, 'experimental': { 'api': { 'batching': { 'renderer': rendererBatchingBaseSettings }, 'breadcrumbs': false, 'iframe': iframeBaseSettings }, 'disableInitialReload': false, 'node': false, 'v2Api': true }, 'frame': true, 'frameConnect': '', 'hideOnBlur': false, 'hideOnClose': false, 'hideWhileChildrenVisible': false, 'icon': '', 'launchExternal': '', 'loadErrorMessage': '', 'maxHeight': -1, 'maxWidth': -1, 'maximizable': true, 'minHeight': 0, 'minWidth': 0, 'minimizable': true, 'name': '', 'opacity': 1, 'plugins': false, 'resizable': true, 'resize': true, 'resizeRegion': { 'bottomRightCorner': shapes_1.DEFAULT_RESIZE_REGION_BOTTOM_RIGHT_CORNER, 'size': shapes_1.DEFAULT_RESIZE_REGION_SIZE, 'sides': shapes_1.DEFAULT_RESIZE_SIDES }, 'saveWindowState': true, 'shadow': false, 'showTaskbarIcon': true, 'smallWindow': false, 'spellCheck': false, 'state': 'normal', 'taskbarIcon': '', 'taskbarIconGroup': '', 'transparent': false, 'url': 'about:blank', 'uuid': '', 'waitForPageLoad': true, 'backgroundColor': '#FFF', 'webSecurity': true }; } function isInContainer(type) { return process && process.versions && process.versions[type]; } function readFile(filePath, done, onError) { log.writeToLog(1, `Requested contents from ${filePath}`, true); let normalizedPath = path.resolve(filePath); log.writeToLog(1, `Normalized path as ${normalizedPath}`, true); fs.readFile(normalizedPath, 'utf8', (err, data) => { if (err) { onError(err); return; } log.writeToLog(1, `Contents from ${normalizedPath}`, true); log.writeToLog(1, data, true); let config; try { config = JSON.parse(data); } catch (e) { onError(e); return; } done(config); }); } function validateOptions(options) { var baseOptions = five0BaseOptions(); if (options.rawWindowOpen) { baseOptions.rawWindowOpen = options.rawWindowOpen; } return validate(baseOptions, options); } function validate(base, user) { let options = {}; _.each(base, (value, key) => { const baseType = typeof base[key]; const userType = typeof user[key]; if (baseType === 'object') { options[key] = validate(base[key], user[key] || {}); } else { options[key] = (userType !== baseType) ? base[key] : user[key]; } }); return options; } function fetchLocalConfig(configUrl, successCallback, errorCallback) { log.writeToLog(1, `Falling back on local-startup-url path: ${configUrl}`, true); readFile(configUrl, configObject => { successCallback({ configObject, configUrl }); }, errorCallback); } module.exports = { getStartupAppOptions: function (appJson) { return appJson['startup_app']; }, convertToElectron: function (options, returnAsString) { const usingIframe = !!(options.api && options.api.iframe); let newOptions = validateOptions(options); if (isInContainer('openfin')) { newOptions.resizable = newOptions.resize && newOptions.resizable; newOptions.show = newOptions.autoShow && !newOptions.waitForPageLoad; newOptions.skipTaskbar = !newOptions.showTaskbarIcon; newOptions.title = newOptions.name; let minHeight = newOptions.minHeight; let maxHeight = newOptions.maxHeight; let defaultHeight = newOptions.defaultHeight; if (defaultHeight < minHeight) { newOptions.height = minHeight; } else if (maxHeight !== -1 && defaultHeight > maxHeight) { newOptions.height = maxHeight; } else { newOptions.height = defaultHeight; } let defaultWidth = newOptions.defaultWidth; let minWidth = newOptions.minWidth; let maxWidth = newOptions.maxWidth; if (defaultWidth < minWidth) { newOptions.width = minWidth; } else if (maxWidth !== -1 && defaultWidth > maxWidth) { newOptions.width = maxWidth; } else { newOptions.width = defaultWidth; } newOptions.center = newOptions.defaultCentered; if (!newOptions.center) { newOptions.x = newOptions.defaultLeft; newOptions.y = newOptions.defaultTop; } } const useNodeInRenderer = newOptions.experimental.node; const noNodePreload = path.join(__dirname, '..', 'renderer', 'node-less.js'); if (usingIframe) { Object.assign(newOptions.experimental.api.iframe, newOptions.api.iframe); } else { newOptions.api.iframe = newOptions.experimental.api.iframe; } if (_.has(options, 'contextMenu')) { newOptions.contextMenuSettings.enable = options.contextMenu; } newOptions.enableLargerThanScreen = true; newOptions['enable-plugins'] = true; newOptions.webPreferences = { api: newOptions.experimental.api, contextMenuSettings: newOptions.contextMenuSettings, disableInitialReload: newOptions.experimental.disableInitialReload, nodeIntegration: false, plugins: newOptions.plugins, preload: (!useNodeInRenderer ? noNodePreload : ''), sandbox: !useNodeInRenderer, spellCheck: newOptions.spellCheck, backgroundThrottling: newOptions.backgroundThrottling }; if (coreState.argo['disable-web-security'] || newOptions.webSecurity === false) { newOptions.webPreferences.webSecurity = false; } if (coreState.argo['user-app-config-args']) { newOptions.userAppConfigArgs = queryString.parse(coreState.argo['user-app-config-args']); } if (options.message !== undefined) { newOptions.message = options.message; } if (options.customData !== undefined) { newOptions.customData = options.customData; } if (options.permissions !== undefined) { newOptions.permissions = options.permissions; } if ('preloadScripts' in options || 'preload' in options) { newOptions.preloadScripts = this.normalizePreloadScripts(options); } if (options.customRequestHeaders !== undefined) { newOptions.customRequestHeaders = options.customRequestHeaders; } if (newOptions.transparent) { newOptions.backgroundColor = TRANSPARENT_WHITE; } if (returnAsString) { return JSON.stringify(newOptions); } else { return JSON.parse(JSON.stringify(newOptions)); } }, fetchOptions: function (argo, onComplete, onError) { let configUrl = (argo['startup-url'] || argo['config']); let localConfigPath = argo['local-startup-url']; let offlineAccess = false; let errorCallback = err => { if (offlineAccess) { fetchLocalConfig(localConfigPath, onComplete, onError); } else { onError(err); } }; if (localConfigPath) { try { let localConfig = JSON.parse(fs.readFileSync(localConfigPath)); if (localConfig['offlineAccess']) { offlineAccess = true; } } catch (err) { log.writeToLog(1, err, true); } } if (typeof configUrl !== 'string') { configUrl = ''; } configUrl = configUrl.replace(/"/g, ''); if (!configUrl) { if (typeof onError === 'function') { onError(new Error('missing runtime argument --startup-url')); } return; } if (main_1.isHttpUrl(configUrl)) { cached_resource_fetcher_1.fetchReadFile(configUrl, true) .then((configObject) => onComplete({ configObject, configUrl })) .catch(errorCallback); return; } let filepath = main_1.isFileUrl(configUrl) ? main_1.uriToPath(configUrl) : configUrl; return readFile(filepath, configObject => { onComplete({ configObject, configUrl }); }, errorCallback); }, normalizePreloadScripts(options) { let preloadScripts = []; if ('preload' in options) { if (typeof options.preload === 'string') { preloadScripts = [{ url: options.preload }]; } else if (Array.isArray(options.preload)) { preloadScripts = options.preload; } } if ('preloadScripts' in options && Array.isArray(options.preloadScripts)) { preloadScripts = options.preloadScripts; } return preloadScripts; } }; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const minimist = require("minimist"); const electron_1 = require("electron"); const external_application_1 = require("./api/external_application"); const log_1 = require("./log"); const frame_1 = require("./api/frame"); const electronIPC = require("./transports/electron_ipc"); const main_1 = require("../common/main"); exports.args = electron_1.app.getCommandLineArguments(); exports.argv = electron_1.app.getCommandLineArgv(); exports.argo = minimist(exports.argv); let apps = []; let startManifest = {}; const manifests = new Map(); let socketServerState = {}; let pendingWindows = []; const manifestProxySettings = { proxyAddress: '', proxyPort: 0, type: 'system' }; function setManifest(url, manifest) { const manifestCopy = JSON.parse(JSON.stringify(manifest)); manifests.set(url, manifestCopy); } exports.setManifest = setManifest; function getManifest(identity) { const uuid = identity && identity.uuid; const url = getConfigUrlByUuid(uuid); const manifest = manifests.get(url); return { url, manifest }; } exports.getManifest = getManifest; function getManifestByUrl(url) { return manifests.get(url); } exports.getManifestByUrl = getManifestByUrl; function getClosestManifest(identity) { const { uuid } = identity; const app = appByUuid(uuid); const url = app && app._configUrl || app.appObj && app.appObj._configUrl; if (url) { const manifest = getManifestByUrl(url); return { url, manifest }; } else { const parentApp = appByUuid(app.parentUuid); return parentApp ? getClosestManifest(parentApp) : null; } } exports.getClosestManifest = getClosestManifest; function setStartManifest(url, data) { startManifest = { url, data }; setManifestProxySettings((data && data.proxy) || undefined); } exports.setStartManifest = setStartManifest; function getStartManifest() { return startManifest; } exports.getStartManifest = getStartManifest; function getEntityInfo(identity) { const entityInfo = getInfoByUuidFrame(identity); if (entityInfo) { return new frame_1.FrameInfo(entityInfo); } else if (external_application_1.ExternalApplication.getExternalConnectionByUuid(identity.uuid)) { const externalAppInfo = external_application_1.ExternalApplication.getInfo(identity); return new frame_1.FrameInfo({ uuid: identity.uuid, entityType: 'external connection', parent: externalAppInfo.parent }); } else { return new frame_1.FrameInfo({ uuid: identity.uuid, name: identity.name, parent: null, entityType: null }); } } exports.getEntityInfo = getEntityInfo; function getExternalOrOfWindowIdentity(identity) { const { uuid, name } = identity; const externalConn = getExternalAppObjByUuid(uuid); if (externalConn) { return Object.assign({}, externalConn, { isExternal: true }); } const ofWindow = getWindowByUuidName(uuid, name); const browserWindow = ofWindow && ofWindow.browserWindow; if (browserWindow && !browserWindow.isDestroyed()) { return { uuid, name, isExternal: false }; } } exports.getExternalOrOfWindowIdentity = getExternalOrOfWindowIdentity; function isLocalUuid(uuid) { const externalConn = getExternalAppObjByUuid(uuid); const app = getAppObjByUuid(uuid); return externalConn || app ? true : false; } exports.isLocalUuid = isLocalUuid; function setManifestProxySettings(proxySettings) { if (typeof proxySettings === 'object') { const type = proxySettings.type; if (!type.includes('system') && !type.includes('named')) { return 'Invalid proxy type. Should be "system" or "named"'; } manifestProxySettings.proxyAddress = proxySettings.proxyAddress || ''; manifestProxySettings.proxyPort = proxySettings.proxyPort || 0; manifestProxySettings.type = type; } } exports.setManifestProxySettings = setManifestProxySettings; function getManifestProxySettings() { return manifestProxySettings; } exports.getManifestProxySettings = getManifestProxySettings; function registerPendingWindowName(uuid, name) { pendingWindows.push({ uuid, name }); } exports.registerPendingWindowName = registerPendingWindowName; function deregisterPendingWindowName(uuid, name) { pendingWindows = pendingWindows.filter(win => !(win.uuid === uuid && win.name === name)); } exports.deregisterPendingWindowName = deregisterPendingWindowName; function windowExists(uuid, name) { const pendingWindowExists = !!pendingWindows.find(win => win.uuid === uuid && win.name === name); return !!getOfWindowByUuidName(uuid, name) || pendingWindowExists; } exports.windowExists = windowExists; function removeChildById(id) { const app = getAppByWin(id); if (app) { app.children.forEach(win => { win.children = win.children.filter(wChildId => { return wChildId !== id; }); }); if (app && app.children) { app.children = app.children.filter(child => { return child.id !== id; }); } } } exports.removeChildById = removeChildById; function getChildrenByWinId(id) { const win = getWinById(id); return win && win.children; } exports.getChildrenByWinId = getChildrenByWinId; function getAppByWin(id) { return apps.find(app => { return !!app.children.find(win => { return win.id === id; }); }); } exports.getAppByWin = getAppByWin; function getAppById(id) { return apps.find(app => app.id === id); } function appByUuid(uuid) { return apps.find(app => uuid === app.uuid); } exports.appByUuid = appByUuid; exports.getAppByUuid = appByUuid; function setAppRunningState(uuid, isRunning) { const app = appByUuid(uuid); if (app) { app.isRunning = isRunning; } } exports.setAppRunningState = setAppRunningState; function getAppRunningState(uuid) { const app = appByUuid(uuid); return app && app.isRunning; } exports.getAppRunningState = getAppRunningState; function getAppRestartingState(uuid) { const app = appByUuid(uuid); return app && app.isRestarting; } exports.getAppRestartingState = getAppRestartingState; function setAppRestartingState(uuid, isRestarting) { const app = appByUuid(uuid); if (app) { app.isRestarting = isRestarting; } } exports.setAppRestartingState = setAppRestartingState; function setAppId(uuid, id) { const app = appByUuid(uuid); if (!app) { console.warn('setAppId - app not found', arguments); return; } app.id = id; app.children = [{ children: [], id: id, openfinWindow: null }]; } exports.setAppId = setAppId; function getAppObjByUuid(uuid) { const app = appByUuid(uuid); return app && app.appObj; } exports.getAppObjByUuid = getAppObjByUuid; function getExternalAppObjByUuid(uuid) { const allExternalConnections = external_application_1.ExternalApplication.getAllExternalConnctions(); return allExternalConnections.find(ea => ea.uuid === uuid); } exports.getExternalAppObjByUuid = getExternalAppObjByUuid; function getUuidBySourceUrl(sourceUrl) { const app = apps.find(app => { const configUrl = app.appObj && app.appObj._configUrl; return configUrl && configUrl === sourceUrl; }); return app && app.appObj && app.appObj.uuid; } exports.getUuidBySourceUrl = getUuidBySourceUrl; function getConfigUrlByUuid(uuid) { const app = getAppAncestor(uuid); if (app && app._configUrl) { return app._configUrl; } else { const externalApp = getExternalAncestor(uuid); return (externalApp && externalApp.configUrl) || ''; } } exports.getConfigUrlByUuid = getConfigUrlByUuid; function setAppObj(id, appObj) { const app = getAppById(id); if (!app) { console.warn('setAppObj - app not found', arguments); return; } if (!appObj) { console.warn('setAppObj - no app object provided', arguments); return; } app.appObj = appObj; return app; } exports.setAppObj = setAppObj; function getAppObj(id) { const app = getAppById(id); if (!app) { console.warn('getAppObj - app not found', arguments); return; } return app.appObj; } exports.getAppObj = getAppObj; function setAppOptions(opts, configUrl = '') { const app = appByUuid(opts.uuid); if (!app) { console.warn('setAppOptions - app not found', arguments); return; } app._configUrl = configUrl; app._options = opts; return app; } exports.setAppOptions = setAppOptions; function getWinById(id) { return getWinList().find(win => win.id === id); } exports.getWinById = getWinById; function getChildrenByApp(id) { const app = getAppById(id); if (!app) { console.warn('getChildrenByApp - app not found', arguments); return; } return app.children .filter(child => child.openfinWindow && child.openfinWindow.name !== child.openfinWindow.uuid) .map(child => child.openfinWindow); } exports.getChildrenByApp = getChildrenByApp; function addChildToWin(parentId, childId) { const app = getAppByWin(parentId); if (!app) { console.warn('addChildToWin - parent app not found', arguments); return; } const parent = getWinById(parentId); if (!parent) { console.warn('addChildToWin - parent window not found', arguments); return; } parent.children.push(childId); return app.children.push({ children: [], id: childId, openfinWindow: null, parentId: parentId }); } exports.addChildToWin = addChildToWin; function getWinObjById(id) { const win = getWinById(id); if (!win) { console.warn('getWinObjById - window not found', arguments); return; } return win.openfinWindow; } exports.getWinObjById = getWinObjById; function addApp(id, uuid) { apps.push({ appObj: null, children: [{ id: id, openfinWindow: null, children: [] }], id: id, isRunning: false, uuid, sentHideSplashScreen: false }); return apps; } exports.addApp = addApp; function sentFirstHideSplashScreen(uuid) { const app = appByUuid(uuid); return app && app.sentHideSplashScreen; } exports.sentFirstHideSplashScreen = sentFirstHideSplashScreen; function setSentFirstHideSplashScreen(uuid, sent) { const app = appByUuid(uuid); if (app) { app.sentHideSplashScreen = sent; } } exports.setSentFirstHideSplashScreen = setSentFirstHideSplashScreen; function setWindowObj(id, openfinWindow) { const win = getWinById(id); if (!win) { console.warn('setWindow - window not found', arguments); return; } if (!openfinWindow) { console.warn('setWindow - no window object provided', arguments); return; } win.openfinWindow = openfinWindow; return win; } exports.setWindowObj = setWindowObj; function removeApp(id) { const app = getAppById(id); if (!app) { console.warn('removeApp - app not found', arguments); return; } delete app.appObj; app.isRunning = false; } exports.removeApp = removeApp; function deleteApp(uuid) { apps = apps.filter(app => app.uuid !== uuid); } exports.deleteApp = deleteApp; function getWindowOptionsById(id) { const win = getWinById(id); return win && win.openfinWindow && win.openfinWindow._options; } exports.getWindowOptionsById = getWindowOptionsById; function getMainWindowOptions(id) { const app = getAppByWin(id); if (!app) { console.warn('getMainWindowOptions - app not found', arguments); return; } if (!app.appObj) { console.warn('getMainWindowOptions - app opts not found', arguments); return; } return app.appObj._options; } exports.getMainWindowOptions = getMainWindowOptions; function getWindowByUuidName(uuid, name) { const win = getOfWindowByUuidName(uuid, name); return win && win.openfinWindow; } exports.getWindowByUuidName = getWindowByUuidName; function getBrowserWindow(identity) { const { uuid, name } = identity; const wnd = getOfWindowByUuidName(uuid, name); if (wnd && wnd.openfinWindow && wnd.openfinWindow.browserWindow && !wnd.openfinWindow.browserWindow.isDestroyed()) { return wnd.openfinWindow.browserWindow; } } exports.getBrowserWindow = getBrowserWindow; function getWebContents(identity) { const browserWindow = getBrowserWindow(identity); if (browserWindow) { return browserWindow.webContents; } } exports.getWebContents = getWebContents; function getSession(identity) { const webContents = getWebContents(identity); if (webContents) { return webContents.session; } } exports.getSession = getSession; function getOfWindowByUuidName(uuid, name) { return getWinList().find(win => win.openfinWindow && win.openfinWindow.uuid === uuid && win.openfinWindow.name === name); } function getWinList() { return apps .map(app => app.children) .reduce((wins, myWins) => wins.concat(myWins), []); } function getAllApplications() { return apps.map(app => { return { isRunning: app.isRunning, parentUuid: app.parentUuid, uuid: app.uuid }; }); } exports.getAllApplications = getAllApplications; function getAllAppObjects() { return apps .filter(app => app.appObj) .map(app => app.appObj); } exports.getAllAppObjects = getAllAppObjects; function getAllWindows() { const windowApi = require('./api/window.js').Window; const aliveApps = apps.filter(({ children }) => { const mainWindow = children[0]; return mainWindow && mainWindow.openfinWindow && mainWindow.openfinWindow.browserWindow && !mainWindow.openfinWindow.browserWindow.isDestroyed(); }); return aliveApps.map(({ uuid, children }) => { const childWindows = children .map(({ openfinWindow }) => { const identity = main_1.getIdentityFromObject(openfinWindow); const bounds = windowApi.getBounds(identity); bounds.name = openfinWindow.name; bounds.state = windowApi.getState(identity); bounds.isShowing = windowApi.isShowing(identity); return bounds; }); const mainWindow = childWindows.shift() || {}; return { childWindows, mainWindow, uuid }; }); } exports.getAllWindows = getAllWindows; function anyAppRestarting() { return !!apps.find(app => app.isRestarting); } function shouldCloseRuntime(ignoreArray) { const ignoredApps = ignoreArray || []; if (anyAppRestarting()) { console.warn('not close Runtime during app restart'); return false; } else { const extConnections = external_application_1.ExternalApplication.getAllExternalConnctions(); const hasPersistentConnections = extConnections.find(conn => conn.nonPersistent === undefined || !conn.nonPersistent); return !hasPersistentConnections && !getAllAppObjects().find(app => { const nonPersistent = app._options.nonPersistent !== undefined ? app._options.nonPersistent : app._options.nonPersistant; return getAppRunningState(app.uuid) && ignoredApps.indexOf(app.uuid) < 0 && !nonPersistent; }); } } exports.shouldCloseRuntime = shouldCloseRuntime; function setSocketServerState(state) { socketServerState = state; } exports.setSocketServerState = setSocketServerState; function getSocketServerState() { return socketServerState; } exports.getSocketServerState = getSocketServerState; function getAppAncestor(descendantAppUuid) { const app = appByUuid(descendantAppUuid); if (app && app.parentUuid) { const parentApp = appByUuid(app.parentUuid); return parentApp ? getAppAncestor(app.parentUuid) : app; } else { return app; } } exports.getAppAncestor = getAppAncestor; function getExternalAncestor(descendantAppUuid) { const app = appByUuid(descendantAppUuid); if (app && app.parentUuid) { return getExternalAncestor(app.parentUuid); } else { return external_application_1.ExternalApplication.getExternalConnectionByUuid(descendantAppUuid); } } function setLicenseKey(identity, licenseKey) { const { uuid } = identity; const app = exports.getAppByUuid(uuid); const externalConnection = external_application_1.ExternalApplication.getExternalConnectionByUuid(uuid); if (app) { app.licenseKey = licenseKey; return licenseKey; } else if (externalConnection) { externalConnection.licenseKey = licenseKey; return licenseKey; } else { return null; } } exports.setLicenseKey = setLicenseKey; function getLicenseKey(identity) { const { uuid } = identity; const app = exports.getAppByUuid(uuid); const externalConnection = external_application_1.ExternalApplication.getExternalConnectionByUuid(uuid); if (app) { return app.licenseKey; } else if (externalConnection) { return externalConnection.licenseKey; } else { return null; } } exports.getLicenseKey = getLicenseKey; function getParentWindow(childIdentity) { const { uuid, name } = childIdentity; const childWin = getOfWindowByUuidName(uuid, name); if (!childWin) { return; } return getWinById(childWin.parentId); } exports.getParentWindow = getParentWindow; function getParentOpenFinWindow(childIdentity) { const parentWin = getParentWindow(childIdentity); if (!parentWin) { return; } return parentWin.openfinWindow; } exports.getParentOpenFinWindow = getParentOpenFinWindow; function getParentIdentity(childIdentity) { const parentOpenFinWin = getParentOpenFinWindow(childIdentity); if (!parentOpenFinWin) { return; } return { uuid: parentOpenFinWin.uuid, name: parentOpenFinWin.name }; } exports.getParentIdentity = getParentIdentity; function getInfoByUuidFrame(targetIdentity) { const { uuid, name: frame } = targetIdentity; const app = appByUuid(uuid); if (!app) { return; } for (const { openfinWindow } of app.children) { if (openfinWindow) { const { name } = openfinWindow; if (name === frame) { const parent = getParentIdentity({ uuid, name }); return { name, uuid, parent, entityType: 'window' }; } else if (openfinWindow.frames.get(frame)) { return openfinWindow.frames.get(frame); } } else { log_1.writeToLog(1, `unable to find openfinWindow of child of ${app.uuid}`, true); } } } exports.getInfoByUuidFrame = getInfoByUuidFrame; function getRoutingInfoByUuidFrame(uuid, frame) { const app = appByUuid(uuid); if (!app) { return; } for (const { openfinWindow } of app.children) { if (openfinWindow) { const { uuid, name } = openfinWindow; let browserWindow; browserWindow = openfinWindow.browserWindow; if (!openfinWindow.mainFrameRoutingId) { if (!browserWindow.isDestroyed()) { openfinWindow.mainFrameRoutingId = browserWindow.webContents.mainFrameRoutingId; log_1.writeToLog(1, `set mainFrameRoutingId ${uuid} ${name} ${openfinWindow.mainFrameRoutingId}`, true); } else { log_1.writeToLog(1, `unable to set mainFrameRoutingId ${uuid} ${name}`, true); } } if (name === frame) { return { name, browserWindow, frameRoutingId: openfinWindow.mainFrameRoutingId, mainFrameRoutingId: openfinWindow.mainFrameRoutingId, frameName: name }; } else if (openfinWindow.frames.get(frame)) { const { name, frameRoutingId } = openfinWindow.frames.get(frame); return { name, browserWindow, frameRoutingId, mainFrameRoutingId: openfinWindow.mainFrameRoutingId, frameName: name }; } } else { log_1.writeToLog(1, `unable to find openfinWindow of child of ${app.uuid}`, true); } } } exports.getRoutingInfoByUuidFrame = getRoutingInfoByUuidFrame; function getWindowInitialOptionSet(windowId) { const ofWin = getWinObjById(windowId); const options = ofWin._options; const { uuid, name } = options; const entityInfo = getEntityInfo({ uuid, name }); const elIPCConfig = { channels: electronIPC.channels }; const socketServerState = getSocketServerState(); const enableChromiumBuild = main_1.isEnableChromiumBuild(); return { options, entityInfo, elIPCConfig, enableChromiumBuild, socketServerState, frames: Array.from(ofWin.frames.values()) }; } exports.getWindowInitialOptionSet = getWindowInitialOptionSet; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const safe_int_1 = require("../common/safe_int"); const utils_1 = require("./utils"); const isWin32 = process.platform === 'win32'; function handleMove(windowId, bounds) { const browserWindow = electron_1.BrowserWindow.fromId(windowId); if (isWin32 && browserWindow && (browserWindow.isMinimized() || browserWindow.isMaximized())) { const oldBounds = browserWindow.getBounds(); const newBounds = { x: safe_int_1.toSafeInt(bounds.x, oldBounds.x), y: safe_int_1.toSafeInt(bounds.y, oldBounds.y), width: safe_int_1.toSafeInt(bounds.w, oldBounds.width), height: safe_int_1.toSafeInt(bounds.h, oldBounds.height) }; browserWindow.setWindowPlacement(utils_1.clipBounds(newBounds, browserWindow)); browserWindow.emit('bounds-changed'); } } exports.handleMove = handleMove; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const of_events_1 = require("./of_events"); const route_1 = require("../common/route"); const window_groups_1 = require("./window_groups"); const WindowTransaction = require('electron').windowTransaction; const window_groups_runtime_proxy_1 = require("./window_groups_runtime_proxy"); const rectangle_1 = require("./rectangle"); const normalized_rectangle_1 = require("./normalized_rectangle"); const log_1 = require("./log"); const isWin32 = process.platform === 'win32'; const getState = (browserWindow) => { if (browserWindow && browserWindow.isMinimized()) { return 'minimized'; } else if (browserWindow && browserWindow.isMaximized()) { return 'maximized'; } else { return 'normal'; } }; const moveToRect = ({ rect }) => rect; var ChangeType; (function (ChangeType) { ChangeType[ChangeType["POSITION"] = 0] = "POSITION"; ChangeType[ChangeType["SIZE"] = 1] = "SIZE"; ChangeType[ChangeType["POSITION_AND_SIZE"] = 2] = "POSITION_AND_SIZE"; })(ChangeType || (ChangeType = {})); const listenerCache = new Map(); const groupInfoCache = new Map(); async function emitChange({ ofWin, rect, offset }, changeType, reason) { const eventBounds = normalized_rectangle_1.getEventBounds(rect, offset); const eventArgs = Object.assign({}, eventBounds, { changeType, reason, deferred: true }); raiseEvent(ofWin, 'bounds-changed', eventArgs); } async function raiseEvent(ofWin, topic, payload) { const uuid = ofWin.uuid; const name = ofWin.name; const id = { uuid, name }; const eventName = route_1.default.window(topic, uuid, name); const eventArgs = Object.assign({}, payload, { uuid, name, topic, type: 'window' }); if (ofWin.isProxy) { const rt = await window_groups_runtime_proxy_1.getRuntimeProxyWindow(id); const fin = rt.hostRuntime.fin; await fin.System.executeOnRemote(id, { action: 'raise-event', payload: { eventName, eventArgs } }); } else { of_events_1.default.emit(eventName, eventArgs); } } function updateGroupedWindowBounds(win, delta) { const shift = Object.assign({}, normalized_rectangle_1.zeroDelta, delta); return handleApiMove(win, shift); } exports.updateGroupedWindowBounds = updateGroupedWindowBounds; function setNewGroupedWindowBounds(win, partialBounds) { const { rect, offset } = normalized_rectangle_1.moveFromOpenFinWindow(win); const bounds = Object.assign({}, normalized_rectangle_1.applyOffset(rect, offset), partialBounds); const newBounds = normalized_rectangle_1.normalizeExternalBounds(bounds, offset); const delta = rect.delta(newBounds); return handleApiMove(win, delta); } exports.setNewGroupedWindowBounds = setNewGroupedWindowBounds; async function handleApiMove(win, delta) { const { rect, offset } = normalized_rectangle_1.moveFromOpenFinWindow(win); const newBounds = rect.shift(delta); if (!rect.moved(newBounds)) { return; } const moved = (delta.x && delta.x + delta.width) || (delta.y && delta.y + delta.height); const resized = delta.width || delta.height; const changeType = resized ? moved ? ChangeType.POSITION_AND_SIZE : ChangeType.SIZE : ChangeType.POSITION; const moves = handleBoundsChanging(win, {}, normalized_rectangle_1.applyOffset(newBounds, offset), changeType); const { leader, otherWindows } = moves.reduce((accum, move) => { move.ofWin === win ? accum.leader = move : accum.otherWindows.push(move); return accum; }, { otherWindows: [] }); if (!leader || leader.rect.moved(newBounds)) { throw new Error('Attempted move violates group constraints'); } handleBatchedMove(moves); await Promise.all([ emitChange(leader, changeType, 'self'), ...otherWindows.map(move => emitChange(move, changeType, 'group')) ]); return leader.rect; } function handleBatchedMove(moves, bringWinsToFront = false) { if (isWin32) { const { flag: { noZorder, noSize, noActivate, inCommit } } = WindowTransaction; const flags = noZorder + noActivate; const wt = new WindowTransaction.Transaction(0); moves.forEach(({ ofWin, rect, offset }) => { const hwnd = parseInt(ofWin.browserWindow.nativeId, 16); wt.setWindowPos(hwnd, Object.assign({}, normalized_rectangle_1.getTransactionBounds(rect, offset), { flags })); if (bringWinsToFront) { ofWin.browserWindow.bringToFront(); } log_1.writeToLog(1, `JavaLog handleBatchedMove ${ofWin.name} delta ${JSON.stringify(offset)} `, true); }); wt.commit(); } else { moves.forEach(({ ofWin, rect, offset }) => { ofWin.browserWindow.setBounds(normalized_rectangle_1.applyOffset(rect, offset)); if (bringWinsToFront) { ofWin.browserWindow.bringToFront(); } }); } } const makeTranslate = (delta) => ({ ofWin, rect, offset }) => { return { ofWin, rect: rect.shift(delta), offset }; }; function getInitialPositions(win, excludeSelf) { return window_groups_1.default.getGroup(win.groupUuid).filter(w => { return (!excludeSelf || w.id !== win.id); }).map(w => { return normalized_rectangle_1.moveFromOpenFinWindow(w); }); } function handleBoundsChanging(win, e, rawPayloadBounds, changeType) { const initialPositions = getInitialPositions(win, !!rawPayloadBounds.original); log_1.writeToLog(1, `JavaLog handleBoundsChanging ${initialPositions.length}`, true); let moves; const startMove = normalized_rectangle_1.moveFromOpenFinWindow(win, rawPayloadBounds.original); const start = startMove.rect; const { offset } = startMove; const end = normalized_rectangle_1.normalizeExternalBounds(rawPayloadBounds, offset); log_1.writeToLog(1, `JavaLog handleBoundsChanging start ${JSON.stringify(start)} end ${JSON.stringify(end)}`, true); switch (changeType) { case ChangeType.POSITION: moves = handleMoveOnly(start, end, initialPositions); break; case ChangeType.SIZE: moves = handleResizeOnly(startMove, end, initialPositions); break; case ChangeType.POSITION_AND_SIZE: const delta = start.delta(end); const xShift = delta.x ? delta.x + delta.width : 0; const yShift = delta.y ? delta.y + delta.height : 0; const shift = { x: xShift, y: yShift, width: 0, height: 0 }; const resizeDelta = { x: delta.x - xShift, y: delta.y - yShift, width: delta.width, height: delta.height }; moves = (delta.width || delta.height) ? handleResizeOnly(startMove, start.shift(resizeDelta), initialPositions, !!(xShift || yShift)) : initialPositions; moves = (xShift || yShift) ? handleMoveOnly(start, start.shift(shift), moves) : moves; break; default: { moves = []; } break; } return moves; } function handleResizeOnly(startMove, end, initialPositions, willShift = false) { const start = startMove.rect; const win = startMove.ofWin; let leaderRect; const numRects = initialPositions.length; const rectPositions = []; for (let i = 0; i < numRects; i++) { const { rect } = initialPositions[i]; if (rect.hasIdenticalBounds(start)) { leaderRect = i; } rectPositions.push(rect); } const windowGraph = rectangle_1.Rectangle.GRAPH(rectPositions); const distances = rectangle_1.Rectangle.DISTANCES(windowGraph, leaderRect); const allMoves = initialPositions .map(({ ofWin, rect, offset }, index) => { let rectFinalPosition = rect; const cachedBounds = rectangle_1.Rectangle.CREATE_FROM_BOUNDS(start); const currentBounds = rectangle_1.Rectangle.CREATE_FROM_BOUNDS(end); let crossedEdges = rect.crossedEdgesBeyondThreshold(cachedBounds, currentBounds); const hasCrossedEdges = crossedEdges.length > 0; const endRect = rectangle_1.Rectangle.CREATE_FROM_BOUNDS(end); const initiallyReachable = distances.get(index) < Infinity; if (rectFinalPosition.hasIdenticalBounds(cachedBounds)) { rectFinalPosition = currentBounds; } else { if (initiallyReachable) { rectFinalPosition = rect.move(start, end); crossedEdges = rectFinalPosition.crossedEdgesBeyondThreshold(cachedBounds, currentBounds); if (crossedEdges.length > 0) { rectFinalPosition = rectFinalPosition.alignCrossedEdges(crossedEdges, endRect); } } else if (hasCrossedEdges) { rectFinalPosition = rect.alignCrossedEdges(crossedEdges, endRect); } } return { ofWin, rect: rectFinalPosition, offset }; }); const moves = allMoves.filter((move, i) => initialPositions[i].rect.moved(move.rect) || willShift); const graphInitial = rectangle_1.Rectangle.GRAPH_WITH_SIDE_DISTANCES(initialPositions.map(moveToRect)); const graphFinal = rectangle_1.Rectangle.GRAPH_WITH_SIDE_DISTANCES(allMoves.map(moveToRect)); if (!rectangle_1.Rectangle.SUBGRAPH_AND_CLOSER(graphInitial, graphFinal)) { return []; } const endMove = moves.find(({ ofWin }) => ofWin === win); if (!endMove) { return []; } const final = endMove.rect; const xChangedWithoutWidth = final.width === start.width && final.x !== start.x; if (xChangedWithoutWidth) { return []; } const yChangedWithoutHeight = final.height === start.height && final.y !== start.y; if (yChangedWithoutHeight) { return []; } return moves; } function handleMoveOnly(start, end, initialPositions) { const delta = start.delta(end); log_1.writeToLog(1, `JavaLog handleMoveOnly delta ${JSON.stringify(delta)}`, true); if (delta.x !== 0 || delta.y !== 0 || delta.height !== 0 || delta.width !== 0) { return initialPositions .map(makeTranslate(delta)); } else { return []; } } function getGroupInfoCacheForWindow(win) { let groupInfo = groupInfoCache.get(win.groupUuid); if (!groupInfo) { groupInfo = { boundsChanging: false, payloadCache: [] }; if (win.groupUuid) { groupInfoCache.set(win.groupUuid, groupInfo); } } return groupInfo; } exports.getGroupInfoCacheForWindow = getGroupInfoCacheForWindow; function addWindowToGroup(win) { log_1.writeToLog(1, `JavaLog addWindowToGroup ${win.name} setUserMovementEnabled false `, true); win.browserWindow.setUserMovementEnabled(false); const listener = async (e, rawPayloadBounds, changeType) => { try { const groupInfo = getGroupInfoCacheForWindow(win); if (groupInfo.boundsChanging) { if (rawPayloadBounds.original) { log_1.writeToLog(1, `JavaLog addWindowToGroup no queue ${win.name} ${JSON.stringify(rawPayloadBounds)}`, true); const moves = handleBoundsChanging(win, e, rawPayloadBounds, changeType); handleBatchedMove(moves); } else { groupInfo.payloadCache.push([win, e, rawPayloadBounds, changeType]); log_1.writeToLog(1, `JavaLog addWindowToGroup queue ${win.name}`, true); } } else { const uuid = win.uuid; const name = win.name; const eventBounds = normalized_rectangle_1.getEventBounds(rawPayloadBounds.original ? rawPayloadBounds.original : win.browserWindow.getBounds()); const moved = new Set(); groupInfo.boundsChanging = true; await raiseEvent(win, 'begin-user-bounds-changing', Object.assign({}, eventBounds, { windowState: getState(win.browserWindow) })); const initialMoves = handleBoundsChanging(win, e, rawPayloadBounds, changeType); handleBatchedMove(initialMoves, true); groupInfo.interval = setInterval(() => { try { if (groupInfo.payloadCache.length) { const [a, b, c, d] = groupInfo.payloadCache.pop(); const moves = handleBoundsChanging(a, b, c, d); groupInfo.payloadCache = []; handleBatchedMove(moves); moves.forEach((move) => { moved.add(move.ofWin); }); } } catch (error) { log_1.writeToLog('error', error); } }, 16); win.browserWindow .once('disabled-frame-bounds-changed', async (e, rawPayloadBounds, changeType) => { try { log_1.writeToLog(1, `JavaLog addWindowToGroup disabled-frame-bounds-changed ${win.name} ${JSON.stringify(rawPayloadBounds)}`, true); clearInterval(groupInfo.interval); groupInfo.payloadCache = []; groupInfo.boundsChanging = false; const moves = handleBoundsChanging(win, e, rawPayloadBounds, changeType); handleBatchedMove(moves); const promises = []; moved.forEach((movedWin) => { const endPosition = normalized_rectangle_1.moveFromOpenFinWindow(movedWin); const isLeader = movedWin === win; promises.push(emitChange(endPosition, changeType, isLeader ? 'self' : 'group')); if (isLeader) { promises.push(raiseEvent(movedWin, 'end-user-bounds-changing', Object.assign({}, normalized_rectangle_1.getEventBounds(endPosition.rect, endPosition.offset), { windowState: getState(win.browserWindow) }))); } }); await promises; } catch (error) { log_1.writeToLog('error', error); } }); } } catch (error) { log_1.writeToLog('error', error); } }; listenerCache.set(win.browserWindow.nativeId, listener); win.browserWindow.on('disabled-frame-bounds-changing', listener); } exports.addWindowToGroup = addWindowToGroup; function removeWindowFromGroup(win) { if (!win.browserWindow.isDestroyed()) { log_1.writeToLog(1, `JavaLog removeWindowFromGroup ${win.name} setUserMovementEnabled true `, true); win.browserWindow.setUserMovementEnabled(true); const winId = win.browserWindow.nativeId; const listener = listenerCache.get(winId); if (listener) { win.browserWindow.removeListener('disabled-frame-bounds-changing', listener); } listenerCache.delete(winId); } } exports.removeWindowFromGroup = removeWindowFromGroup; function deleteGroupInfoCache(groupUuid) { groupInfoCache.delete(groupUuid); } exports.deleteGroupInfoCache = deleteGroupInfoCache; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); let ofEvents = require('./of_events.js').default; let electronApp = require('electron').app; let MonitorInfo; electronApp.on('ready', () => { MonitorInfo = require('./monitor_info.js'); }); const route_1 = require("../common/route"); const log_1 = require("./log"); class ExternalWindowEventAdapter { constructor(browserWindow) { let options = browserWindow && browserWindow._options; let uuid = options.uuid; let name = options.name; let disabledFrameState = { leftButtonDown: false, cursorStart: null, cursorPrev: null, boundsStart: null }; let cachedState = null; ofEvents.on(route_1.default.externalWindow('focus', uuid, name), () => { browserWindow.emit('focus'); }); ofEvents.on(route_1.default.externalWindow('blur', uuid, name), () => { browserWindow.emit('blur'); }); ofEvents.on(route_1.default.externalWindow('state-change', uuid, name), () => { let prevState = cachedState || 'normal'; let currState = 'normal'; if (browserWindow.isMinimized()) { currState = 'minimized'; } else if (browserWindow.isMaximized()) { currState = 'maximized'; } if (prevState !== currState) { if (currState === 'minimized') { browserWindow.emit('minimize'); } else if (currState === 'maximized') { browserWindow.emit('maximize'); } else { browserWindow.emit('restore'); } cachedState = currState; } }); ofEvents.on(route_1.default.externalWindow('bounds-changed', uuid, name), () => { browserWindow.emit('bounds-changed'); }); ofEvents.on(route_1.default.externalWindow('bounds-changing', uuid, name), (bounds) => { browserWindow.emit('bounds-changing', {}, bounds); }); ofEvents.on(route_1.default.externalWindow('visibility-changed', uuid, name), (visibility) => { browserWindow.emit('visibility-changed', {}, visibility); }); ofEvents.on(route_1.default.externalWindow('begin-user-bounds-change', uuid, name), (coordinates) => { log_1.writeToLog(1, `JavaLog begin-user-bounds-change ${name} ${disabledFrameState.leftButtonDown} ${browserWindow.isUserMovementEnabled()}`, true); if (!disabledFrameState.leftButtonDown && !browserWindow.isUserMovementEnabled()) { disabledFrameState.leftButtonDown = true; disabledFrameState.cursorStart = { x: coordinates.x, y: coordinates.y }; disabledFrameState.cursorPrev = disabledFrameState.cursorStart; disabledFrameState.boundsStart = browserWindow.getBounds(); } browserWindow.emit('begin-user-bounds-change'); }); ofEvents.on(route_1.default.externalWindow('end-user-bounds-change', uuid, name), (endBounds) => { log_1.writeToLog(1, `JavaLog end-user-bounds-change ${name} ${disabledFrameState.leftButtonDown} ${browserWindow.isUserMovementEnabled()}`, true); if (disabledFrameState.leftButtonDown) { if (disabledFrameState.changeType !== -1) { browserWindow.emit('disabled-frame-bounds-changed', {}, endBounds ? endBounds : browserWindow.getBounds(), disabledFrameState.changeType); } disabledFrameState.leftButtonDown = false; disabledFrameState.changeType = -1; } browserWindow.emit('end-user-bounds-change'); }); ofEvents.on(route_1.default.externalWindow('sizing', uuid, name), (bounds) => { if (disabledFrameState.leftButtonDown) { if (disabledFrameState.changeType !== 2) { let xDelta = bounds.x !== disabledFrameState.boundsStart.x; let yDelta = bounds.y !== disabledFrameState.boundsStart.y; disabledFrameState.changeType = xDelta || yDelta ? 2 : 1; } browserWindow.emit('disabled-frame-bounds-changing', {}, bounds, disabledFrameState.changeType); } }); ofEvents.on(route_1.default.externalWindow('moving', uuid, name), (movingBounds) => { log_1.writeToLog(1, `JavaLog on moving ${name} leftButton ${disabledFrameState.leftButtonDown} bounds ${JSON.stringify(movingBounds)} `, true); if (disabledFrameState.leftButtonDown) { let bounds = browserWindow.getBounds(); let mousePosition = MonitorInfo.getMousePosition(); let cursorCurr = { x: mousePosition.left, y: mousePosition.top }; disabledFrameState.changeType = 0; let xCursorDelta = cursorCurr.x - disabledFrameState.cursorPrev.x; let yCursorDelta = cursorCurr.y - disabledFrameState.cursorPrev.y; if (movingBounds.original) { browserWindow.emit('disabled-frame-bounds-changing', {}, movingBounds, disabledFrameState.changeType); } else if (xCursorDelta !== 0 || yCursorDelta !== 0) { bounds.x = (cursorCurr.x - disabledFrameState.cursorStart.x) + disabledFrameState.boundsStart.x; bounds.y = (cursorCurr.y - disabledFrameState.cursorStart.y) + disabledFrameState.boundsStart.y; browserWindow.emit('disabled-frame-bounds-changing', {}, bounds, disabledFrameState.changeType); disabledFrameState.cursorPrev = cursorCurr; } else { log_1.writeToLog(1, `JavaLog on moving ignoring ${name} bounds ${JSON.stringify(movingBounds)} `, true); } } else { browserWindow.emit('bounds-changing', {}, movingBounds); } }); ofEvents.on(route_1.default.externalWindow('close', uuid, name), () => { browserWindow.emit('close'); browserWindow.emit('will-close'); browserWindow.close(); browserWindow.emit('closed'); }); } } module.exports = ExternalWindowEventAdapter; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); class IntPool { constructor() { this.currInt = 1; this.releasedBuffer = []; } next() { let next; if (this.releasedBuffer.length) { next = this.releasedBuffer.shift(); } else { next = this.currInt; this.currInt += 1; } return next; } release(releasedInt) { if (this.releasedBuffer.indexOf(releasedInt) === -1) { this.releasedBuffer.push(releasedInt); this.releasedBuffer.sort(); } } } exports.default = new IntPool(); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const errors_1 = require("../common/errors"); exports.logLevelMappings = new Map([ ['verbose', -1], ['info', 0], ['warning', 1], ['error', 2], ['fatal', 3], [-1, 'verbose'], [0, 'info'], [1, 'warning'], [2, 'error'], [3, 'fatal'] ]); function writeToLog(level, message, debug) { const isObj = typeof message === 'object' && message !== null; let parsedMessage; try { if (isObj && message instanceof Error) { parsedMessage = JSON.stringify(errors_1.errorToPOJO(message)); } else if (isObj && message.toString === Object.prototype.toString) { const className = message.constructor && message.constructor.name; parsedMessage = JSON.stringify(message); if (className !== 'Object' && className !== 'Function') { parsedMessage = `${className}: ${parsedMessage}`; } } else { parsedMessage = message + ''; } } catch (err) { return err; } if (debug) { return electron_1.app.vlog(level, parsedMessage); } else { return electron_1.app.log(level, parsedMessage); } } exports.writeToLog = writeToLog; function setToVerbose() { const verboseLogLevel = exports.logLevelMappings.get('verbose'); electron_1.app.setMinLogLevel(verboseLogLevel); } exports.setToVerbose = setToVerbose; let EventEmitter = require('events').EventEmitter; let util = require('util'); let _ = require('underscore'); let electronScreen = require('electron').screen; const isWin32 = process.platform === 'win32'; function MonitorInfo() { EventEmitter.call(this); this._callback = (reason) => { return () => { var monitorInfo = this.getInfo(reason); monitorInfo.topic = 'system'; monitorInfo.type = 'monitor-info-changed'; this.emit('monitor-info-changed', monitorInfo); }; }; electronScreen.on('display-added', this._callback('display')); electronScreen.on('display-removed', this._callback('display')); electronScreen.on('work-area-changed', (event, changedMetrics) => { changedMetrics.forEach((metric) => { if (metric === 'bounds') { this._callback('display')(); } else if (metric === 'workArea') { this._callback('taskbar')(); } else { this._callback('unknown')(); } }); }); } util.inherits(MonitorInfo, EventEmitter); MonitorInfo.prototype.getMousePosition = function () { var point = electronScreen.getCursorScreenPoint(); return { top: point.y, left: point.x }; }; MonitorInfo.prototype.getInfo = function (reason) { var taskbar = electronScreen.getTaskbarLocation(), taskbarInfo = getTaskbarInfo(taskbar), taskbarEdge = getTaskbarEdge(taskbar, taskbarInfo); var monitorDetails = electronScreen.getMonitorDPIScaling(), allMonitorsInfo = getAllMonitorsInfo(monitorDetails); var primaryMonitorId = electronScreen.getPrimaryDisplay().id, primaryMonitor, nonPrimaryMonitors; if (isWin32) { const primaryMonitorName = (monitorDetails[primaryMonitorId] || {}).name; primaryMonitor = allMonitorsInfo.find(e => e.name === primaryMonitorName); nonPrimaryMonitors = allMonitorsInfo.filter(e => e.name !== primaryMonitorName); } else { primaryMonitor = allMonitorsInfo.find(e => e.deviceId === primaryMonitorId); nonPrimaryMonitors = allMonitorsInfo.filter(e => e.deviceId !== primaryMonitorId); } var virtualScreenInfo = getVirtualScreenInfo(allMonitorsInfo); return { deviceScaleFactor: electronScreen.getDPIScale(), dpi: { x: electronScreen.getDPI().width, y: electronScreen.getDPI().height }, nonPrimaryMonitors: nonPrimaryMonitors, primaryMonitor: primaryMonitor, reason: reason, taskbar: { dipRect: taskbarInfo.unscaled, edge: taskbarEdge, rect: taskbarInfo.unscaled, scaledRect: taskbarInfo.scaled }, virtualScreen: { top: virtualScreenInfo.unscaled.top, bottom: virtualScreenInfo.unscaled.bottom, left: virtualScreenInfo.unscaled.left, right: virtualScreenInfo.unscaled.right, dipRect: virtualScreenInfo.unscaled, scaledRect: virtualScreenInfo.scaled } }; }; MonitorInfo.prototype.getNearestDisplayRoot = function (point) { let screen = electronScreen.getDisplayNearestPoint(point); return { x: screen.workArea.x, y: screen.workArea.y }; }; function getTaskbarInfo(taskbar) { var scaled = { top: taskbar.y, bottom: taskbar.y + taskbar.height, left: taskbar.x, right: taskbar.x + taskbar.width }, unscaledRect = electronScreen.screenToDIPRect(taskbar), unscaled = { top: unscaledRect.y, bottom: unscaledRect.y + unscaledRect.height, left: unscaledRect.x, right: unscaledRect.x + unscaledRect.width }; return { scaled, unscaled }; } function getTaskbarEdge(taskbar, taskbarInfo) { if (taskbar.width > taskbar.height) { return taskbarInfo.unscaled.top === 0 ? 'top' : 'bottom'; } else { return taskbarInfo.unscaled.left === 0 ? 'left' : 'right'; } } function getAllMonitorsInfo(monitorDetails) { return electronScreen.getAllDisplays().filter((monitor) => { return isWin32 ? monitorDetails[monitor.id] : true; }).map((monitor) => { var monitorBoundsInfo = isWin32 ? monitorDetails[monitor.id] : monitor; var available = { top: monitorBoundsInfo.workArea.y, bottom: monitorBoundsInfo.workArea.y + monitorBoundsInfo.workArea.height, left: monitorBoundsInfo.workArea.x, right: monitorBoundsInfo.workArea.x + monitorBoundsInfo.workArea.width }, availableScaledRect = electronScreen.dipToScreenRect(monitorBoundsInfo.workArea), total = { top: monitorBoundsInfo.bounds.y, bottom: monitorBoundsInfo.bounds.y + monitorBoundsInfo.bounds.height, left: monitorBoundsInfo.bounds.x, right: monitorBoundsInfo.bounds.x + monitorBoundsInfo.bounds.width }, totalScaledRect = electronScreen.dipToScreenRect(monitorBoundsInfo.bounds); return { available: { dipRect: _.clone(available), scaledRect: { top: availableScaledRect.y, bottom: availableScaledRect.y + availableScaledRect.height, left: availableScaledRect.x, right: availableScaledRect.x + availableScaledRect.width } }, availableRect: _.clone(available), deviceId: monitorBoundsInfo.id, deviceScaleFactor: monitorBoundsInfo.dpiScale, displayDeviceActive: monitorBoundsInfo.active, dpi: { x: monitorBoundsInfo.dpi && monitorBoundsInfo.dpi.width, y: monitorBoundsInfo.dpi && monitorBoundsInfo.dpi.height }, monitor: { dipRect: total, scaledRect: { top: totalScaledRect.y, bottom: totalScaledRect.y + totalScaledRect.height, left: totalScaledRect.x, right: totalScaledRect.x + totalScaledRect.width } }, monitorRect: total, name: monitorBoundsInfo.name }; }); } function getVirtualScreenInfo(allMonitorsInfo) { let uTop = _.min(allMonitorsInfo, monitor => monitor && monitor.monitorRect && monitor.monitorRect.top); let uBottom = _.max(allMonitorsInfo, monitor => monitor && monitor.monitorRect && monitor.monitorRect.bottom); let uLeft = _.min(allMonitorsInfo, monitor => monitor && monitor.monitorRect && monitor.monitorRect.left); let uRight = _.max(allMonitorsInfo, monitor => monitor && monitor.monitorRect && monitor.monitorRect.right); let unscaled = { top: uTop && uTop.monitorRect ? uTop.monitorRect.top : 0, bottom: uBottom && uBottom.monitorRect ? uBottom.monitorRect.bottom : 0, left: uLeft && uLeft.monitorRect ? uLeft.monitorRect.left : 0, right: uRight && uRight.monitorRect ? uRight.monitorRect.right : 0 }; let unscaledRect = { x: unscaled.left, y: unscaled.top, width: unscaled.right - unscaled.left, height: unscaled.bottom - unscaled.top }; let scaledRect = electronScreen.dipToScreenRect(unscaledRect); let scaled = { top: scaledRect.y, bottom: scaledRect.y + scaledRect.height, left: scaledRect.x, right: scaledRect.x + scaledRect.width }; return { scaled, unscaled }; } module.exports = new MonitorInfo(); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const coreState = require('./core_state'); const electronApp = require('electron').app; const subscription_manager_1 = require("./subscription_manager"); const of_events_1 = require("./of_events"); const route_1 = require("../common/route"); const main_1 = require("../common/main"); const subscriptionManager = new subscription_manager_1.default(); function validateNavigation(webContents, identity, validator) { const willNavigateString = 'will-navigate'; webContents.on(willNavigateString, validator); const willNavigateUnsubscribe = () => { webContents.removeListener(willNavigateString, validator); }; subscriptionManager.registerSubscription(willNavigateUnsubscribe, identity, willNavigateString); } exports.validateNavigation = validateNavigation; function validateNavigationRules(uuid, url, parentUuid, baseOpts) { electronApp.vlog(1, `validateNavigationRules for ${uuid} to ${url}`); let isAllowed = true; if (baseOpts.contentNavigation) { if (baseOpts.contentNavigation.whitelist) { isAllowed = electronApp.matchesURL(url, baseOpts.contentNavigation.whitelist); } else if (baseOpts.contentNavigation.blacklist) { isAllowed = !electronApp.matchesURL(url, baseOpts.contentNavigation.blacklist); } } if (!isAllowed) { electronApp.vlog(1, `Navigation is blocked by rules for ${baseOpts.uuid} to ${url}`); return false; } else if (parentUuid) { electronApp.vlog(1, `validateNavigationRules app ${uuid} check parent ${parentUuid}`); const parentObject = coreState.appByUuid(parentUuid); if (parentObject && parentObject.isRunning) { const parentOpts = parentObject.appObj._options; isAllowed = validateNavigationRules(uuid, url, parentObject.parentUuid, parentOpts); } else { electronApp.vlog(1, `validateNavigationRules missing parent ${parentUuid}`); } } else { electronApp.vlog(1, `validateNavigationRules no parent ${uuid}`); } return isAllowed; } exports.validateNavigationRules = validateNavigationRules; function navigationValidator(uuid, name, id) { return (event, url) => { const appObject = coreState.getAppObjByUuid(uuid); const appMetaInfo = coreState.appByUuid(uuid); const isMailTo = /^mailto:/i.test(url); const allowed = isMailTo || validateNavigationRules(uuid, url, appMetaInfo.parentUuid, appObject._options) && main_1.isURLAllowed(url); if (!allowed) { electronApp.vlog(1, 'Navigation is blocked ' + url, true); const self = coreState.getWinById(id); let sourceName = name; if (self.parentId) { const parent = coreState.getWinById(self.parentId); if (parent) { const parentOpts = coreState.getWindowOptionsById(parent.id); if (parentOpts) { sourceName = parentOpts.name; } } } of_events_1.default.emit(route_1.default.window('navigation-rejected', uuid, name), { name, uuid, url, sourceName }); event.preventDefault(); } }; } exports.navigationValidator = navigationValidator; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const rectangle_1 = require("./rectangle"); const system_1 = require("./api/system"); const log_1 = require("./log"); const osName = system_1.System.getHostSpecs().name; const isWin10 = /Windows 10/.test(osName); function negate(delta) { return { x: -delta.x, y: -delta.y, height: -delta.height, width: -delta.width }; } exports.negate = negate; const framedOffset = { x: 7, y: 0, height: -7, width: -14 }; exports.zeroDelta = { x: 0, y: 0, height: 0, width: 0 }; function moveFromOpenFinWindow(ofWin, original) { const win = ofWin.browserWindow; const delta = isWin10 && win._options.frame ? framedOffset : exports.zeroDelta; const normalizedOptions = Object.assign({}, win._options); if (normalizedOptions.maxHeight === -1) { normalizedOptions.maxHeight = Number.MAX_SAFE_INTEGER; } if (normalizedOptions.maxWidth === -1) { normalizedOptions.maxWidth = Number.MAX_SAFE_INTEGER; } if (win._options.frame) { normalizedOptions.minWidth = Math.max(win._options.minWidth, 150); } log_1.writeToLog(1, `JavaLog moveFromOpenFinWindow name ${ofWin.name} bounds ${JSON.stringify(win.getBounds())} \ delta ${JSON.stringify(delta)} `, true); return { ofWin, rect: rectangle_1.Rectangle.CREATE_FROM_BOUNDS(original ? original : win.getBounds(), normalizedOptions).shift(delta), offset: negate(delta) }; } exports.moveFromOpenFinWindow = moveFromOpenFinWindow; function applyOffset(rect, offset = exports.zeroDelta) { return { x: rect.x + offset.x, y: rect.y + offset.y, width: rect.width + offset.width, height: rect.height + offset.height }; } exports.applyOffset = applyOffset; function normalizeExternalBounds(rect, offset) { log_1.writeToLog(1, `JavaLog normalizeExternalBounds rect ${JSON.stringify(rect)} offset ${JSON.stringify(offset)}`, true); return applyOffset(rect, negate(offset)); } exports.normalizeExternalBounds = normalizeExternalBounds; function getEventBounds(rect, offset) { const normalizedBounds = applyOffset(rect, offset); return { left: normalizedBounds.x, top: normalizedBounds.y, width: normalizedBounds.width, height: normalizedBounds.height }; } exports.getEventBounds = getEventBounds; function getTransactionBounds(rect, offset) { const normalizedBounds = applyOffset(rect, offset); return { x: normalizedBounds.x, y: normalizedBounds.y, w: normalizedBounds.width, h: normalizedBounds.height }; } exports.getTransactionBounds = getTransactionBounds; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const events_1 = require("events"); const main_1 = require("../common/main"); const route_1 = require("../common/route"); class OFEvents extends events_1.EventEmitter { constructor() { super(); this.subscriber = { ADDED: 'subscriber-added', REMOVED: 'subscriber-removed' }; this.startTempSaveEvents(); } emit(routeString, ...data) { const tokenizedRoute = routeString.split('/'); const eventPropagations = new Map(); const [payload, maybeOpts, ...otherExtraArgs] = data; if (this.isSavingEvents) { const timestampJs = Date.now(); const timestampNative = electron_1.app.nowFromSystemTime(); this.history.push({ payload, routeString, timestampJs, timestampNative }); } const isMultiRuntimeEvent = maybeOpts && maybeOpts.isMultiRuntime; const extraArgs = isMultiRuntimeEvent ? otherExtraArgs : [maybeOpts, ...otherExtraArgs]; if (tokenizedRoute.length >= 2) { const [channel, topic] = tokenizedRoute; const uuid = (payload && payload.uuid) || tokenizedRoute[2] || '*'; const source = tokenizedRoute.slice(2).join('/'); const envelope = { channel, topic, source, data }; const propagateToSystem = !topic.match(/-requested$/); super.emit(route_1.default(channel, '*'), envelope); if (source) { super.emit(route_1.default(channel, topic, '*'), envelope); super.emit(route_1.default(channel, '*', source), envelope); } const shouldPropagate = (channel === 'window' || channel === 'application') && !isMultiRuntimeEvent; if (shouldPropagate) { const checkedPayload = typeof payload === 'object' ? payload : { payload }; if (channel === 'window') { const propTopic = `window-${topic}`; const dontPropagate = [ 'close-requested' ]; if (!dontPropagate.some(t => t === topic)) { eventPropagations.set(route_1.default.application(propTopic, uuid), Object.assign({}, checkedPayload, { type: propTopic, topic: 'application' })); if (propagateToSystem) { eventPropagations.set(route_1.default.system(propTopic), Object.assign({}, checkedPayload, { type: propTopic, topic: 'system' })); } } } else if (channel === 'application' && propagateToSystem) { const propTopic = `application-${topic}`; const appWindowEventsNotOnWindow = [ 'window-alert-requested', 'window-created', 'window-end-load', 'window-responding', 'window-start-load' ]; if (!topic.match(/^window-/)) { eventPropagations.set(route_1.default.system(propTopic), Object.assign({}, checkedPayload, { type: propTopic, topic: 'system' })); } else if (appWindowEventsNotOnWindow.some(t => t === topic)) { eventPropagations.set(route_1.default.system(topic), Object.assign({}, checkedPayload, { type: topic, topic: 'system' })); } } } } const result = super.emit(routeString, ...data); eventPropagations.forEach((propagationPayload, eventString) => { this.emit(eventString, propagationPayload, ...extraArgs); }); return result; } checkMissedEvents(data, listener) { const { name, timestamp, topic, type, uuid } = data; const routeString = route_1.default[topic](type, uuid, name); this.history.forEach((pastEvent) => { const routeMatches = pastEvent.routeString === routeString; if (routeMatches) { let missedEvent = false; if (Number.isInteger(timestamp)) { missedEvent = pastEvent.timestampJs >= timestamp; } else if (main_1.isFloat(timestamp)) { missedEvent = pastEvent.timestampNative >= timestamp; } if (missedEvent) { listener(pastEvent.payload); } } }); } startTempSaveEvents() { const STARTUP_SAVE_EVENTS_DURATION = 10000; this.history = []; this.isSavingEvents = true; setTimeout(() => { this.history.length = 0; this.isSavingEvents = false; }, STARTUP_SAVE_EVENTS_DURATION); } } exports.default = new OFEvents(); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const transport_1 = require("./transport"); const log = require("./log"); const route_1 = require("../common/route"); const connection_manager_1 = require("./connection_manager"); const coreState = require("./core_state"); const UNIX_FILENAME_PREFIX = '/tmp/of.pd'; const WINDOW_CLASS_NAME = 'OPENFIN_ADAPTER_WINDOW'; class PortDiscovery extends events_1.EventEmitter { constructor() { super(); this.broadcast = (portDiscoveryPayload) => { try { const transport = this.constructTransport(); coreState.setSocketServerState(portDiscoveryPayload); if (transport) { transport.publish(portDiscoveryPayload); } if (portDiscoveryPayload.runtimeInformationChannel) { this._namedPipe = new transport_1.ChromiumIPC(portDiscoveryPayload.runtimeInformationChannel); this._namedPipe.publish({ action: 'runtime-information', payload: portDiscoveryPayload }); } } catch (e) { log.writeToLog('info', `Port Discovery broadcast failed: ${JSON.stringify(e)}`); } }; } constructTransport() { if (!this._transport) { if (process.platform === 'win32') { log.writeToLog('info', 'Constructing the copyDataTransport window.'); this._transport = new transport_1.WMCopyData(WINDOW_CLASS_NAME, WINDOW_CLASS_NAME); } else { log.writeToLog('info', 'Opening and binding to a unix domain socket for port discovery.'); this._transport = new transport_1.UnixDomainSocket(UNIX_FILENAME_PREFIX); } if (this._transport) { this._transport.on('message', (s, data) => { this.emit(route_1.default.runtime('launched'), JSON.parse(data)); }); } } return this._transport; } getPortInfoByArgs(args, port) { const versionKeyword = args['version-keyword']; const securityRealm = args['security-realm']; const runtimeInformationChannel = args['runtime-information-channel-v6']; const ver = process.versions; const multiRuntime = connection_manager_1.isMeshEnabled(args); const portDiscoveryPayload = { version: ver.openfin, sslPort: -1, port, options: args, requestedVersion: versionKeyword, securityRealm, runtimeInformationChannel, multiRuntime }; return portDiscoveryPayload; } } exports.PortDiscovery = PortDiscovery; exports.portDiscovery = new PortDiscovery(); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const window_1 = require("./api/window"); const cached_resource_fetcher_1 = require("./cached_resource_fetcher"); const convert_options_1 = require("./convert_options"); const fs_1 = require("fs"); const log_1 = require("./log"); const coreState = require("./core_state"); const pathMap = new Map(); const getKey = (uuid, url) => `${uuid} ${url}`; async function downloadScripts(identity, preloadScripts = []) { const promises = preloadScripts.map((preloadScript) => { return downloadScript(identity, preloadScript); }); return await Promise.all(promises); } exports.downloadScripts = downloadScripts; function downloadScript(identity, preloadScript) { return new Promise((resolve) => { const { uuid, name } = identity; const { url } = preloadScript; const pathMapKey = getKey(uuid, url); const log = (msg) => { log_1.writeToLog('info', `[preloadScripts] [${uuid}]-[${name}]: ${msg}`); }; log(`Started downloading preload script from URL [${url}]`); cached_resource_fetcher_1.cachedFetch(identity, url, (error, scriptPath) => { const result = { url, success: !error }; if (error) { pathMap.set(pathMapKey, ''); log(`Failed downloading preload script from URL [${url}]: ${error}`); result.error = error.toString(); } else { pathMap.set(pathMapKey, scriptPath); log(`Succeeded downloading preload script from URL [${url}]`); } resolve(result); }); }); } async function loadScripts(identity) { let options; const frameInfo = coreState.getInfoByUuidFrame(identity); if (frameInfo && frameInfo.entityType === 'iframe') { options = window_1.Window.getOptions({ uuid: frameInfo.parent.uuid, name: frameInfo.parent.name }); } else { options = window_1.Window.getOptions(identity); } const preloadScripts = convert_options_1.normalizePreloadScripts(options); const promises = preloadScripts.map((preloadScript) => loadScript(identity, preloadScript)); return await Promise.all(promises); } exports.loadScripts = loadScripts; function loadScript(identity, preloadScript) { return new Promise((resolve) => { const { uuid, name } = identity; const { url } = preloadScript; const pathMapKey = getKey(uuid, url); const scriptPath = pathMap.get(pathMapKey); const log = (msg) => { log_1.writeToLog('info', `[preloadScripts] [${uuid}]-[${name}]: ${msg}`); }; log(`Started loading preload script for URL [${url}]`); window_1.Window.setWindowPreloadState(identity, Object.assign({}, preloadScript, { state: 'load-started' })); if (!scriptPath) { log(`Failed loading preload script for URL [${url}]: preload script wasn't downloaded`); window_1.Window.setWindowPreloadState(identity, Object.assign({}, preloadScript, { state: 'load-failed' })); return resolve(Object.assign({}, preloadScript, { _content: '' })); } fs_1.readFile(scriptPath, 'utf8', (readError, data) => { if (readError) { log(`Failed loading preload script for URL [${url}] from path [${scriptPath}]: ${readError}`); window_1.Window.setWindowPreloadState(identity, Object.assign({}, preloadScript, { state: 'load-failed' })); resolve(Object.assign({}, preloadScript, { _content: '' })); } else { log(`Succeeded loading preload script for URL [${url}] from path [${scriptPath}]`); window_1.Window.setWindowPreloadState(identity, Object.assign({}, preloadScript, { state: 'load-succeeded' })); resolve(Object.assign({}, preloadScript, { _content: data })); } }); }); } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); let fs = require('fs'); let path = require('path'); let eApp = require('electron').app; let ExternalProcess = require('electron').externalProcess; let ProcessMonitor = require('electron').processMonitor; const of_events_1 = require("./of_events"); const route_1 = require("../common/route"); const fileDownload = require("./api/file_download"); const isWin32 = (process.platform === 'win32'); function ProcessTracker() { this._processes = {}; this._uuidToPid = {}; this._windowToUuids = {}; this._processMonitor = new ProcessMonitor(); this._processMonitor.on('process-terminated', (event, handle, pid, exitCode) => { var winUuid = this._processes[pid].window.uuid; var winName = this._processes[pid].window.name; var uuid = this._processes[pid].uuid; var result = { exitCode, processUuid: uuid, }; of_events_1.default.emit(route_1.default.externalApplication('exited', uuid), Object.assign(result, { topic: 'external-application', type: 'exited' })); of_events_1.default.emit(route_1.default.window('external-process-exited', winUuid, winName), Object.assign(result, { uuid: winUuid, name: winName, topic: 'window', type: 'external-process-exited' })); this._cleanup(pid, uuid); }); of_events_1.default.on(route_1.default.window('synth-close', '*'), payload => { if (this._windowToUuids[payload.source]) { let processes = this._windowToUuids[payload.source].slice(0); processes.forEach(uuid => { this.terminate(uuid, 500, true); }); } }); } ProcessTracker.prototype.launch = function (identity, options, errDataCallback) { let eProcess = new ExternalProcess(); let procObj; var uuid = options.uuid || generateUuid(); let success = (data) => { var windowUuid = identity.uuid; var windowName = identity.name; errDataCallback(undefined, data); of_events_1.default.emit(route_1.default.externalApplication('started', data.uuid), { uuid: data.uuid, topic: 'external-application', type: 'started' }); of_events_1.default.emit(route_1.default.window('external-process-started', windowUuid, windowName), { uuid: windowUuid, name: windowName, topic: 'window', type: 'external-process-started', processUuid: data.uuid }); }; let error = (errObj) => { eApp.vlog(1, errObj); errDataCallback(errObj, undefined); }; let withDefaultCertOptions = (certOptions) => { return Object.assign({ publicKey: '', serial: '', subject: '', thumbprint: '', trusted: false }, certOptions); }; let validateCertificate = (filePath, certOptions) => { let response = { publicKey: true, serial: true, subject: true, thumbprint: true, trusted: true, error: undefined }; if (isWin32) { let checkSignatureAndUpdateForError = (nativeSubject, key) => { let valueKey = key || nativeSubject; response[valueKey] = true; let value = certOptions[valueKey]; if (value) { if (!eApp.compareFileSignature(filePath, nativeSubject, value)) { response.error = (response.error || '') + `${valueKey} does not match. `; response[valueKey] = false; } } }; if (certOptions.trusted) { let result = eApp.verifyFileSignature(filePath, 1, 0x10); if (result !== 'success') { response.trusted = false; response.error = `${result}. `; } } checkSignatureAndUpdateForError('publickey', 'publicKey'); checkSignatureAndUpdateForError('serial'); checkSignatureAndUpdateForError('subject'); checkSignatureAndUpdateForError('thumbprint'); } return response; }; let launchProcess = (fpath, args, cwd, certOpts) => { let certResult = validateCertificate(fpath, certOpts); if (!certResult.error) { fpath = expandEnvironmentVars(fpath); args = expandEnvironmentVars(args); cwd = expandEnvironmentVars(cwd); let parentWindowUuidName = getParentWindowUuidName(identity, options.lifetime); procObj = eProcess.launch(fpath, cwd, args, !!parentWindowUuidName); if (!procObj) { return error(`Error attempting to launch '${fpath}'.`); } if (parentWindowUuidName) { let processes = this._windowToUuids[parentWindowUuidName] || []; processes.push(uuid); this._windowToUuids[parentWindowUuidName] = processes; } this._processMonitor.add(procObj); this._processes[procObj.id] = { process: procObj, window: identity, lifetime: options.lifetime, uuid, monitor: true }; this._uuidToPid[uuid] = procObj.id; success({ uuid }); } else { error(certResult.error); } }; if (this._uuidToPid[uuid]) { return error(`Process with specified UUID already exists: ${uuid}`); } if (options.alias) { var appAssetsFetcher = require('./rvm/runtime_initiated_topics/app_assets').appAssetsFetcher; appAssetsFetcher.fetchAppAsset(options.srcUrl, options.alias, (aliasJsonObject) => { var exeArgs = options.arguments || aliasJsonObject.args || ''; var exePath = path.join(aliasJsonObject.path, (options.target || aliasJsonObject.target)); var exeCwd = aliasJsonObject.path || ''; let configCertOptions = options.certificate || {}; let overrideCertOptions = aliasJsonObject.certificate || {}; Object.keys(configCertOptions).forEach((key) => { overrideCertOptions[key] = configCertOptions[key]; }); let certificateOptions = withDefaultCertOptions(overrideCertOptions); fs.stat(exePath, (err, stats) => { if (err) { error(`The app asset doesn\'t seem to exist :( Error: ${err}.`); } else if (!stats.isFile(exePath)) { error('The app asset isn\'t a file.'); } else { try { eApp.vlog(1, JSON.stringify(aliasJsonObject)); } catch (e) { } launchProcess(exePath, exeArgs, exeCwd, certificateOptions); } }); }, () => { error('Could not query application assets.'); }); } else { let args = options.arguments || ''; let filePath = options.target || options.path || ''; let certificateOptions = withDefaultCertOptions(options.certificate); let fileUuid = options.fileUuid; if (fileUuid) { if (fileDownload.hasAccess(identity, fileUuid)) { const FileDownloadLocation = fileDownload.downloadLocationMap.get(fileUuid); if (FileDownloadLocation) { filePath = FileDownloadLocation.path; } } else { error(`Identity uuid:${uuid} does not have access to fileUuid: ${fileUuid}`); } } if (filePath) { if (path.isAbsolute(filePath)) { fs.stat(filePath, (err) => { if (err) { error('file not found'); } else { launchProcess(filePath, args, '', certificateOptions); } }); } else { launchProcess(filePath, args, '', certificateOptions); } } else { error('Target was not defined.'); } } }; ProcessTracker.prototype.monitor = function (winIdentity, options) { let { pid: pidRequested, uuid: uuidRequested, lifetime, monitor: monitorRequested, } = options; let pid = parseInt(pidRequested, 10); let processEntry = this._processes[pid] || {}; if (isNaN(pid)) { throw new Error(`Error monitoring external process, invalid pid value specified.`); } if (monitorRequested && processEntry.monitor) { throw new Error(`Error monitoring external process, already monitoring pid: '${pid}'.`); } if (uuidRequested && processEntry.uuid && processEntry.uuid !== uuidRequested) { throw new Error(`Error monitoring external process, pid '${pid}' previously assigned a different UUID.`); } let uuid = processEntry.uuid || uuidRequested || generateUuid(); let monitor = processEntry.monitor || monitorRequested; let eProcess = new ExternalProcess(); let parentWindowUuidName = getParentWindowUuidName(winIdentity, lifetime); let procObj = eProcess.attach(pid); if (!procObj.handle) { return; } if (parentWindowUuidName) { let processes = this._windowToUuids[parentWindowUuidName] || []; processes.push(uuid); this._windowToUuids[parentWindowUuidName] = processes; } this._processMonitor.add(procObj); this._processes[pid] = { process: procObj, window: winIdentity, lifetime, uuid, monitor }; this._uuidToPid[uuid] = pid; return { uuid }; }; ProcessTracker.prototype.release = function (uuid) { let pid = this._uuidToPid[uuid]; if (!pid) { throw new Error(`Error releasing external process, no match for UUID '${uuid}'`); } if (this._processes[pid].lifetime && this._processes[pid].lifetime !== 'persist') { throw new Error(`Error releasing external process, cannot release nonpersistent processes`); } this._processes[pid].monitor = false; }; ProcessTracker.prototype.terminate = function (uuid, timeout, child) { var pid = this._uuidToPid[uuid]; if (!pid) { throw new Error(`Error terminating external process, no match for UUID '${uuid}'`); } return this._processes[pid].process.terminate(timeout, child); }; ProcessTracker.prototype.getProcessByUuid = function (uuid) { var pid = this._uuidToPid[uuid]; return pid ? this._processes[pid] : null; }; ProcessTracker.prototype.getProcessByPid = function (pid) { return this._processes[pid]; }; ProcessTracker.prototype._cleanup = function (pid, uuid) { let winIdentity = this._processes[pid].window; let lifetime = this._processes[pid].lifetime; let parentWindowUuidName = getParentWindowUuidName(winIdentity, lifetime); if (parentWindowUuidName && this._windowToUuids[parentWindowUuidName]) { let index = this._windowToUuids[parentWindowUuidName].indexOf(uuid); if (index !== -1) { this._windowToUuids[parentWindowUuidName].splice(index, 1); } if (this._windowToUuids[parentWindowUuidName].length === 0) { delete this._windowToUuids[parentWindowUuidName]; } } delete this._processes[pid]; delete this._uuidToPid[uuid]; }; function generateUuid() { return eApp.generateGUID(); } function expandEnvironmentVars(str) { let replacementFn = (match, p1) => { return process.env[p1] || match; }; if (isWin32) { return str.replace(/%(.+)%/g, replacementFn); } else { return str.replace(/\$([\w]+)/g, replacementFn).replace(/\$\{(.+)\}/g, replacementFn); } } function getParentWindowUuidName(winIdentity, lifetime) { let result; switch (lifetime) { case 'application': result = `${winIdentity.uuid}-${winIdentity.uuid}`; break; case 'window': result = `${winIdentity.uuid}-${winIdentity.name}`; break; case 'persist': default: result = null; break; } return result; } module.exports = new ProcessTracker(); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); class RectOptionsOpts { constructor(opts) { this.minWidth = Math.max(opts.minWidth || 48, 48); this.maxWidth = opts.maxWidth || Number.MAX_SAFE_INTEGER; this.minHeight = Math.max(opts.minHeight || 38, 38); this.maxHeight = opts.maxHeight || Number.MAX_SAFE_INTEGER; } } const zeroDelta = { x: 0, y: 0, height: 0, width: 0 }; class Rectangle { constructor(x, y, width, height, opts = {}) { this.grow = (h, v) => { let x = this.x; let y = this.y; let width = this.width; let height = this.height; width += x; height += y; x -= h; y -= v; width += h; height += v; if (width < x) { width -= x; if (width < Number.MIN_SAFE_INTEGER) { width = Number.MIN_SAFE_INTEGER; } if (x < Number.MIN_SAFE_INTEGER) { x = Number.MIN_SAFE_INTEGER; } else if (x > Number.MAX_VALUE) { x = Number.MAX_VALUE; } } else { if (x < Number.MIN_SAFE_INTEGER) { x = Number.MIN_SAFE_INTEGER; } else if (x > Number.MAX_VALUE) { x = Number.MAX_VALUE; } width -= x; if (width < Number.MIN_SAFE_INTEGER) { width = Number.MIN_SAFE_INTEGER; } else if (width > Number.MAX_VALUE) { width = Number.MAX_VALUE; } } if (height < y) { height -= y; if (height < Number.MIN_SAFE_INTEGER) { height = Number.MIN_SAFE_INTEGER; } if (y < Number.MIN_SAFE_INTEGER) { y = Number.MIN_SAFE_INTEGER; } else if (y > Number.MAX_VALUE) { y = Number.MAX_VALUE; } } else { if (y < Number.MIN_SAFE_INTEGER) { y = Number.MIN_SAFE_INTEGER; } else if (y > Number.MAX_VALUE) { y = Number.MAX_VALUE; } height -= y; if (height < Number.MIN_SAFE_INTEGER) { height = Number.MIN_SAFE_INTEGER; } else if (height > Number.MAX_VALUE) { height = Number.MAX_VALUE; } } return new Rectangle(x, y, width, height, this.opts); }; this.collidesWith = (rect) => { const { x, y, width, height } = rect; let collision = false; if (this.x < x + width && this.x + this.width > x && this.y < y + height && this.y + this.height > y) { collision = true; } return collision; }; this.sharedBound = (side, rect) => { let delta; let oppositeDelta; switch (side) { case 'top': case 'bottom': { delta = 'top'; oppositeDelta = 'bottom'; } break; case 'left': case 'right': { delta = 'left'; oppositeDelta = 'right'; } } if (Math.abs(this[side] - rect[delta]) <= Rectangle.BOUND_SHARE_THRESHOLD) { return delta; } if (Math.abs(this[side] - rect[oppositeDelta]) <= Rectangle.BOUND_SHARE_THRESHOLD) { return oppositeDelta; } return null; }; this.sharedBounds = (rect) => { let top = this.sharedBound('top', rect); let right = this.sharedBound('right', rect); let bottom = this.sharedBound('bottom', rect); let left = this.sharedBound('left', rect); let hasSharedBounds = !!(top || right || bottom || left); return { hasSharedBounds, top, right, bottom, left }; }; this.sharedBoundsOnIntersection = (rect) => { const growth = Rectangle.BOUND_SHARE_THRESHOLD; const intersectionRect = rect.grow(growth, growth); const intersection = this.collidesWith(intersectionRect); let hasSharedBounds = false; let top = null; let right = null; let bottom = null; let left = null; if (!intersection) { return { hasSharedBounds, top, right, bottom, left }; } else { return this.sharedBounds(rect); } }; this.sharedBoundsList = (rect) => { const sides = ['top', 'right', 'left', 'bottom']; const sharedBounds = this.sharedBounds(rect); return sides.map(side => { const correspondingSide = sharedBounds[side]; let pair; if (correspondingSide) { pair = [side, correspondingSide]; } return pair; }).filter(x => x); }; this.moved = (rect) => { return !(rect.x === this.x && rect.y === this.y && rect.height === this.height && rect.width === this.width); }; this.delta = (rect) => { return { x: rect.x - this.x, y: rect.y - this.y, width: rect.width - this.width, height: rect.height - this.height }; }; this.outerBounds = (rect) => { return { x: Math.min(rect.x, this.x), y: Math.min(rect.y, this.y), width: Math.max(rect.width, this.width), height: Math.max(rect.height, this.height) }; }; this.edgeMoved = (pair, delta) => { const { x, y, width, height } = delta; const [mySide, otherRectSharedSide] = pair; const movedSides = new Set(); if (!x && width) { movedSides.add('right'); } if (x && width) { movedSides.add('left'); } if (!y && height) { movedSides.add('bottom'); } if (y && height) { movedSides.add('top'); } return movedSides.has(otherRectSharedSide); }; this.alignSide = (mySide, rect, sideToAlign) => { const changes = this.bounds; switch (mySide) { case 'left': { changes.width += (this.x - rect[sideToAlign]); const tooSmall = changes.width < this.opts.minWidth; const tooBig = changes.width > this.opts.maxWidth; changes.x = rect[sideToAlign]; if (tooSmall) { changes.width = this.opts.minWidth; } else if (tooBig) { changes.width = this.opts.maxWidth; } } break; case 'right': { changes.width += (rect[sideToAlign] - (this.x + this.width)); if (changes.width < this.opts.minWidth) { changes.x = rect[sideToAlign] - this.opts.minWidth; changes.width = this.opts.minWidth; } else if (changes.width > this.opts.maxWidth) { changes.x = rect[sideToAlign] - this.opts.maxWidth; changes.width = this.opts.maxWidth; } } break; case 'top': { changes.height += (this.y - rect[sideToAlign]); const tooSmall = changes.height < this.opts.minHeight; const tooBig = changes.height > this.opts.maxHeight; changes.y = rect[sideToAlign]; if (tooSmall) { changes.height = this.opts.minHeight; } else if (tooBig) { changes.height = this.opts.maxHeight; } } break; case 'bottom': { changes.height += (rect[sideToAlign] - (this.y + this.height)); if (changes.height < this.opts.minHeight) { changes.y = rect[sideToAlign] - this.opts.minHeight; changes.height = this.opts.minHeight; } else if (changes.height > this.opts.maxHeight) { changes.y = rect[sideToAlign] - this.opts.maxHeight; changes.height = this.opts.maxHeight; } } break; default: return null; } return Rectangle.CREATE_FROM_BOUNDS(changes, this.opts); }; this.shift = (delta) => { return new Rectangle(this.x + delta.x, this.y + delta.y, this.width + delta.width, this.height + delta.height, this.opts); }; this.crossedEdges = (initialBounds, finalBounds) => { const positionsInitial = this.relativePositions(initialBounds); const positionsFinal = this.relativePositions(finalBounds); const crossedBounds = []; const currentBoundsCollide = this.collidesWith(finalBounds); const resizedOutOfContainingRect = (!currentBoundsCollide && this.collidesWith(initialBounds)); const adjacentOnAtLeastOneSide = this.sharedBoundsOnIntersection(finalBounds).hasSharedBounds; if (currentBoundsCollide || resizedOutOfContainingRect || adjacentOnAtLeastOneSide) { for (let i = 0; i < Rectangle.EDGE_CROSSINGS.length; i++) { if (positionsInitial[i] !== positionsFinal[i]) { crossedBounds.push(Rectangle.EDGE_CROSSINGS[i]); } } } let xCrossing; let yCrossing; for (let [mine, other] of crossedBounds) { const distance = Math.abs(this[mine] - finalBounds[other]); if (mine === 'left' || mine === 'right') { if (!xCrossing || distance > xCrossing.distance) { xCrossing = { mine, other, distance }; } } else { if (!yCrossing || distance > yCrossing.distance) { yCrossing = { mine, other, distance }; } } } return [xCrossing, yCrossing].filter(x => x); }; this.crossedEdgesBeyondThreshold = (initialBounds, finalBounds) => { const crossedEdges = this.crossedEdges(initialBounds, finalBounds); return crossedEdges.filter(({ distance }) => distance > Rectangle.BOUND_SHARE_THRESHOLD); }; this.alignCrossedEdges = (edgeCrossings, finalOtherBounds) => { let rect = this; for (const crossing of edgeCrossings) { rect = rect.alignSide(crossing.mine, finalOtherBounds, crossing.other); } return rect; }; this.relativePositions = (rect) => { return Rectangle.EDGE_CROSSINGS.map(([mySide, otherSide]) => this[mySide] > rect[otherSide]); }; this.move = (cachedBounds, currentBounds) => { const sharedBoundsList = this.sharedBoundsList(Rectangle.CREATE_FROM_BOUNDS(cachedBounds)); const currLeader = Rectangle.CREATE_FROM_BOUNDS(currentBounds); const delta = Rectangle.CREATE_FROM_BOUNDS(cachedBounds).delta(currLeader); let rect = this; for (const [thisRectSharedSide, otherRectSharedSide] of sharedBoundsList) { if (rect.edgeMoved([thisRectSharedSide, otherRectSharedSide], delta)) { rect = rect.alignSide(thisRectSharedSide, currLeader, otherRectSharedSide); } } return rect; }; this.adjacent = (rects) => { return Array.from(Rectangle.ADJACENCY_LIST([...rects, this]).values()).find(list => list.includes(this)); }; this.hasIdenticalBounds = (rect) => { return this.x === rect.x && this.y === rect.y && this.width === rect.width && this.height === rect.height; }; this.x = x; this.y = y; this.width = width; this.height = height; this.opts = new RectOptionsOpts(opts); } static CREATE_FROM_BOUNDS(rect, opts = {}) { const { x, y, width, height } = rect; return new Rectangle(x, y, width, height, new RectOptionsOpts(opts)); } get right() { return this.x + this.width; } get bottom() { return this.y + this.height; } get top() { return this.y; } get left() { return this.x; } get bounds() { return { x: this.x, y: this.y, height: this.height, width: this.width }; } static ADJACENCY_LIST(rects) { const adjLists = new Map(); const rectLen = rects.length; for (let i = 0; i < rectLen; i++) { const adjacentRects = []; const rect = rects[i]; for (let ii = 0; ii < rectLen; ii++) { if (i !== ii) { if (rect.sharedBoundsOnIntersection(rects[ii]).hasSharedBounds) { adjacentRects.push(ii); } } } adjLists.set(i, adjacentRects); } return adjLists; } static GRAPH_WITH_SIDE_DISTANCES(rects) { const edges = new Set(); const edgeDistances = new Map(); const vertices = new Set(); const rectLen = rects.length; for (let i = 0; i < rectLen; i++) { const rect = rects[i]; vertices.add(i); for (let ii = 0; ii < rectLen; ii++) { if (i !== ii) { if (rect.sharedBoundsOnIntersection(rects[ii]).hasSharedBounds) { const sharedBoundsList = rect.sharedBoundsList(rects[ii]); sharedBoundsList.forEach((sides) => { const [mySide, otherSide] = sides; const key = [i, ii, Side[mySide], Side[otherSide]].toString(); edges.add(key); edgeDistances.set(key, Math.abs(rect[mySide] - rects[ii][otherSide])); }); } } } } return { vertices, edges, edgeDistances }; } static SUBGRAPH_AND_CLOSER(a, b) { for (const v of a.vertices) { if (!b.vertices.has(v)) { return false; } } for (const e of a.edges) { if (!b.edges.has(e)) { return false; } else if (b.edgeDistances.get(e) > a.edgeDistances.get(e)) { return false; } } return true; } static sharedBoundValidator(rect1, rect2) { return rect1.sharedBoundsOnIntersection(rect2).hasSharedBounds; } static collisionsValidator(rect1, rect2) { return rect1.collidesWith(rect2); } static GRAPH(rects, validator = Rectangle.sharedBoundValidator) { const edges = []; const vertices = []; const rectLen = rects.length; for (let i = 0; i < rectLen; i++) { const rect = rects[i]; vertices.push(i); for (let ii = 0; ii < rectLen; ii++) { if (i !== ii) { if (validator(rects[i], rects[ii])) { edges.push([i, ii]); } } } } return [vertices, edges]; } static DISTANCES(graph, refV) { const [vertices, edges] = graph; const distances = new Map(); for (let v in vertices) { distances.set(+v, Infinity); } distances.set(refV, 0); const toVisit = [refV]; while (toVisit.length) { const u = toVisit.shift(); const e = edges.filter(([uu]) => uu === u); e.forEach(([u, v]) => { if (distances.get(v) === Infinity) { toVisit.push(v); distances.set(v, distances.get(u) + 1); } }); } return distances; } static BREADTH_WALK(graph, refV) { const [vertices, edges] = graph; const distances = new Map(); for (let v in vertices) { distances.set(+v, []); } distances.set(refV, [refV]); const toVisit = [refV]; while (toVisit.length) { const u = toVisit.shift(); const e = edges.filter(([uu]) => uu === u); e.forEach(([u, v]) => { if (distances.get(v).slice(-1)[0] !== v) { toVisit.push(v); const d = distances.get(u).concat(distances.get(v)); d.push(v); distances.set(v, d); } }); } return distances; } } Rectangle.BOUND_SHARE_THRESHOLD = 5; Rectangle.EDGE_CROSSINGS = [ ['top', 'top'], ['top', 'bottom'], ['bottom', 'top'], ['bottom', 'bottom'], ['right', 'left'], ['right', 'right'], ['left', 'left'], ['left', 'right'] ]; exports.Rectangle = Rectangle; var Side; (function (Side) { Side[Side["top"] = 0] = "top"; Side[Side["right"] = 1] = "right"; Side[Side["bottom"] = 2] = "bottom"; Side[Side["left"] = 3] = "left"; })(Side || (Side = {})); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const main_1 = require("../common/main"); const connection_manager_1 = require("./connection_manager"); const of_events_1 = require("./of_events"); const route_1 = require("../common/route"); let subscriptionIdCount = 0; const pendingRemoteSubscriptions = new Map(); const systemEventsToIgnore = { 'idle-state-changed': true, 'monitor-info-changed': true, 'session-changed': true }; function addRemoteSubscription(subscriptionProps) { return new Promise(resolve => { const clonedProps = JSON.parse(JSON.stringify(subscriptionProps)); const subscription = Object.assign(clonedProps, { isCleaned: false, timestamp: electron_1.app.nowFromSystemTime() }); if (!subscription._id) { subscription._id = getId(); } if (typeof subscription.unSubscriptions !== 'object') { subscription.unSubscriptions = new Map(); } connection_manager_1.default.resolveIdentity({ uuid: subscription.uuid }).then((id) => { applyRemoteSubscription(subscription, id.runtime); }).catch(() => { pendingRemoteSubscriptions.set(subscription._id, subscription); connection_manager_1.default.connections.forEach((runtime) => { applyRemoteSubscription(subscription, runtime); }); }).then(() => { const unsubscribe = cleanUpSubscription.bind(null, subscription); resolve(unsubscribe); }); }); } exports.addRemoteSubscription = addRemoteSubscription; async function applyRemoteSubscription(subscription, runtime) { const classEventEmitter = await getClassEventEmitter(subscription, runtime); const runtimeKey = connection_manager_1.keyFromPortInfo(runtime.portInfo); const { uuid, name, className, eventName, listenType, timestamp } = subscription; let { unSubscriptions } = subscription; const fullEventName = (typeof name === 'string') ? route_1.default(className, eventName, uuid, name, true) : route_1.default(className, eventName, uuid); if (!(unSubscriptions instanceof Map)) { unSubscriptions = new Map(); subscription.unSubscriptions = unSubscriptions; } const listener = (data) => { if (!data.runtimeUuid) { data.runtimeUuid = connection_manager_1.getMeshUuid(); of_events_1.default.emit(fullEventName, data, { isMultiRuntime: true }); } cleanUpSubscription(subscription, runtimeKey); }; classEventEmitter[listenType](eventName, listener, { timestamp }); if (!Array.isArray(unSubscriptions.get(runtimeKey))) { unSubscriptions.set(runtimeKey, []); } unSubscriptions.get(runtimeKey).push(() => { const unsub = classEventEmitter.removeListener; unsub(eventName, listener); }); } function cleanUpSubscription(subscription, keepInRuntimeVersion) { if (subscription.isCleaned && keepInRuntimeVersion) { return; } connection_manager_1.default.connections.forEach((runtime) => { const runtimeKey = connection_manager_1.keyFromPortInfo(runtime.portInfo); if (runtimeKey === keepInRuntimeVersion) { persistSubscription(subscription, runtime); return; } unSubscribe(subscription, runtime); }); pendingRemoteSubscriptions.delete(subscription._id); subscription.isCleaned = true; } function persistSubscription(subscription, runtime) { const runtimeKey = connection_manager_1.keyFromPortInfo(runtime.portInfo); const { unSubscriptions } = subscription; const disconnectEventName = 'disconnected'; const listener = () => { unSubscribe(subscription, runtime); addRemoteSubscription(subscription); }; runtime.fin.on(disconnectEventName, listener); unSubscriptions.get(runtimeKey).push(() => { runtime.fin.removeListener(disconnectEventName, listener); }); } function unSubscribe(subscription, runtime) { const runtimeKey = connection_manager_1.keyFromPortInfo(runtime.portInfo); const { unSubscriptions } = subscription; const unSubs = unSubscriptions.get(runtimeKey); if (unSubs) { unSubs.forEach(unSubscribe => unSubscribe()); } unSubscriptions.delete(runtimeKey); } function applyAllRemoteSubscriptions(runtime) { pendingRemoteSubscriptions.forEach(subscription => { if (!subscription.isSystemEvent) { applyRemoteSubscription(subscription, runtime); } else { applySubscriptionToAllRuntimes(subscription, runtime); } }); } exports.applyAllRemoteSubscriptions = applyAllRemoteSubscriptions; function subscribeToAllRuntimes(subscriptionProps) { return new Promise(resolve => { if (systemEventsToIgnore[subscriptionProps.eventName]) { return resolve(main_1.noop); } const clonedProps = JSON.parse(JSON.stringify(subscriptionProps)); const subscription = Object.assign(clonedProps, { isSystemEvent: true, timestamp: electron_1.app.nowFromSystemTime() }); subscription._id = getId(); subscription.unSubscriptions = new Map(); if (connection_manager_1.default.connections.length) { connection_manager_1.default.connections.forEach(runtime => applySubscriptionToAllRuntimes(subscription, runtime)); } pendingRemoteSubscriptions.set(subscription._id, subscription); const unsubscribe = systemUnsubscribe.bind(null, subscription); resolve(unsubscribe); }); } exports.subscribeToAllRuntimes = subscribeToAllRuntimes; function systemUnsubscribe(subscription) { subscription.unSubscriptions.forEach((runtime) => { if (runtime.length) { runtime.forEach((removeListener) => removeListener()); } }); pendingRemoteSubscriptions.delete(subscription._id); } function applySubscriptionToAllRuntimes(subscription, runtime) { const { className, eventName, listenType, timestamp } = subscription; const fullEventName = route_1.default(className, eventName); const runtimeKey = connection_manager_1.keyFromPortInfo(runtime.portInfo); const listener = (data) => { if (!data.runtimeUuid) { data.runtimeUuid = connection_manager_1.getMeshUuid(); of_events_1.default.emit(fullEventName, data, { isMultiRuntime: true }); } }; if (className === 'system') { runtime.fin.System[listenType](eventName, listener, { timestamp }); } else if (className === 'channel') { runtime.fin.InterApplicationBus.Channel[listenType](eventName, listener, { timestamp }); } const disconnectEventName = 'disconnected'; const unSubscribeListener = () => { unSubscribe(subscription, runtime); }; runtime.fin.on(disconnectEventName, unSubscribeListener); if (!Array.isArray(subscription.unSubscriptions.get(runtimeKey))) { subscription.unSubscriptions.set(runtimeKey, []); } if (className === 'system') { subscription.unSubscriptions.get(runtimeKey).push(() => { runtime.fin.System.removeListener(eventName, listener); runtime.fin.removeListener(disconnectEventName, unSubscribeListener); }); } else if (className === 'channel') { subscription.unSubscriptions.get(runtimeKey).push(() => { runtime.fin.InterApplicationBus.Channel.removeListener(eventName, listener); runtime.fin.removeListener(disconnectEventName, unSubscribeListener); }); } } async function getClassEventEmitter(subscription, runtime) { let classEventEmitter; const { uuid, name, className } = subscription; switch (className) { case 'application': classEventEmitter = await runtime.fin.Application.wrap({ uuid }); break; case 'window': classEventEmitter = await runtime.fin.Window.wrap({ uuid, name }); break; } return classEventEmitter; } function getId() { return ++subscriptionIdCount; } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const EventEmitter = require("events"); const evt = { nodeAdded: 'node-added', nodeRemoved: 'node-removed' }; class EmitterMap extends EventEmitter { constructor() { super(); this.set = (key, node) => { this._map.set(key, node); this.emit(evt.nodeAdded, key, node); }; this.remove = (key) => { const node = this._map.get(key); this._map.delete(key); this.emit(evt.nodeRemoved, key, node); }; this.get = (key) => { return this._map.get(key); }; this._map = new Map(); } get size() { return this._map.size; } [Symbol.iterator]() { return this._map.entries(); } } exports.EmitterMap = EmitterMap; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const emitter_map_1 = require("./emitter_map"); const crypto_1 = require("crypto"); const main_1 = require("../../../js-adapter/src/main"); const events_1 = require("events"); class PeerConnectionManager extends events_1.EventEmitter { constructor() { super(); this.genRuntimeKey = (params) => { return crypto_1.createHash('md4') .update(params.version) .update('' + params.port) .digest('base64'); }; this.genIdentityKey = (identity) => { return crypto_1.createHash('md4') .update(identity.uuid) .digest('base64'); }; this.connectToRuntime = (uuid, portInfo) => { return new Promise((resolve, reject) => { const key = this.genRuntimeKey(portInfo); if (this._runtimeMap.get(key)) { reject(new Error('Already connected to runtime')); return; } if (this._pendingConnectionMap.get(key)) { reject(new Error('Already connecting to runtime')); return; } this._pendingConnectionMap.set(key, true); main_1.connect({ address: `ws://localhost:${portInfo.port}`, uuid, runtimeClient: true, nonPersistent: true }).then((fin) => { const peerRt = { fin, portInfo, isDisconnected: false }; this._runtimeMap.set(key, peerRt); this._pendingConnectionMap.delete(key); resolve(peerRt); }); }); }; this._runtimeMap = new emitter_map_1.EmitterMap(); this._identityMap = new emitter_map_1.EmitterMap(); this._pendingConnectionMap = new Map(); this._runtimeMap.on('node-added', (key, peer) => { const onDisconnect = () => { for (const kvPair of this._identityMap) { if (key === this.genRuntimeKey(kvPair[1].runtime.portInfo)) { this._identityMap.remove(kvPair[0]); } } peer.fin.removeListener('disconnected', onDisconnect); peer.isDisconnected = true; this._runtimeMap.remove(key); }; peer.fin.on('disconnected', onDisconnect); }); } get connections() { const rtList = []; for (const kvPair of this._runtimeMap) { rtList.push(kvPair[1]); } return rtList; } resolveIdentity(identity) { const identityKey = this.genIdentityKey(identity); return new Promise((resolve, reject) => { let identityAddress = this._identityMap.get(identityKey); if (identityAddress) { resolve(identityAddress); } else { let failures = 0; if (this._runtimeMap.size < 1) { reject(new Error('No Connections')); } const checkingConnectionSize = this._runtimeMap.size; for (const kvPair of this._runtimeMap) { kvPair[1].fin.System.resolveUuid(identity.uuid).then(() => { identityAddress = { identity, runtime: kvPair[1] }; this._identityMap.set(identityKey, identityAddress); resolve(identityAddress); }).catch((err) => { failures++; if (failures >= checkingConnectionSize) { reject(err); } }); } } }); } } exports.PeerConnectionManager = PeerConnectionManager; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const rvm_message_bus_1 = require("../rvm_message_bus"); const log = require("../../log"); const _ = require("underscore"); class AppAssetsFetcher { constructor() { this.pendingRequests = {}; this.addPendingRequest = (sourceUrl, assetAlias, successCB, failureCB) => { const pendingCBObj = { successCB, failureCB }; if (!(sourceUrl in this.pendingRequests)) { this.pendingRequests[sourceUrl] = {}; this.pendingRequests[sourceUrl][assetAlias] = [pendingCBObj]; return true; } else { this.pendingRequests[sourceUrl][assetAlias] = this.pendingRequests[sourceUrl][assetAlias] || []; this.pendingRequests[sourceUrl][assetAlias].push(pendingCBObj); return false; } }; this.responseHandler = (dataObj) => { let sourceUrl; const timeToLiveExpired = _.has(dataObj, 'time-to-live-expiration'); if (timeToLiveExpired) { sourceUrl = dataObj.envelope.payload.appConfig; log.writeToLog(1, `Time to live of ${dataObj['time-to-live-expiration']} seconds for app asset request for ` + `app config: ${sourceUrl} reached.`, true); dataObj.error = `Unable to determine app asset information for ${sourceUrl}`; } else { log.writeToLog(1, `AppAssetsFetcher received a response from RVM: ${dataObj}`, true); if (!this.isResponseValid(dataObj)) { return; } sourceUrl = dataObj.appConfig; } this.notifyObservers(sourceUrl, dataObj); if (_.isString(sourceUrl)) { delete this.pendingRequests[sourceUrl]; } }; this.isResponseValid = (dataObj) => { const hasSourceUrl = _.has(dataObj, 'appConfig'); const hasResult = _.has(dataObj, 'result'); const hasError = _.has(dataObj, 'error'); if (!hasSourceUrl || !(hasResult || hasError)) { log.writeToLog(1, 'Invalid request from the RVM, missing required fields.', true); return false; } const waitingForThisResponse = _.has(this.pendingRequests, dataObj.appConfig); if (!waitingForThisResponse) { log.writeToLog(1, `Received app assets response from RVM for ${dataObj.appConfig} but we have no pending requests`, true); return false; } return true; }; this.notifyObservers = (sourceUrl, dataObj) => { if (_.has(dataObj, 'error')) { this.handleErrorResponse(sourceUrl, dataObj); } else { this.handleInfoResponse(sourceUrl, dataObj); } }; this.handleErrorResponse = (sourceUrl, dataObj) => { log.writeToLog(1, `Received error for ${sourceUrl}, Error: ${dataObj.error}`); _.each(this.pendingRequests[sourceUrl], (requestedAliasCallbackArray) => { _.invoke(requestedAliasCallbackArray, 'failureCB', dataObj.error); }); }; this.handleInfoResponse = (sourceUrl, dataObj) => { _.mapObject(this.pendingRequests[sourceUrl], (requestedAliasCallbackArray, alias) => { const aliasInResponse = _.findWhere(dataObj.result, { alias }); if (aliasInResponse) { _.invoke(requestedAliasCallbackArray, 'successCB', aliasInResponse); } else { _.invoke(requestedAliasCallbackArray, 'failureCB', 'Found no information on requested alias ' + alias); } }); }; } fetchAppAsset(sourceUrl, assetAlias, successCB, failureCB) { if (!sourceUrl) { log.writeToLog(1, 'sourceUrl is required!', true); } else if (!assetAlias) { log.writeToLog(1, 'assetAlias is required!', true); } else if (!successCB) { log.writeToLog(1, 'successCB is required!', true); } else if (!failureCB) { log.writeToLog(1, 'failureCB is required!', true); } else { const firstRequest = this.addPendingRequest(sourceUrl, assetAlias, successCB, failureCB); if (firstRequest) { const msg = { timeToLive: 7, topic: 'app-assets', type: 'get-list', appConfig: sourceUrl }; rvm_message_bus_1.rvmMessageBus.publish(msg, this.responseHandler); } } } } const appAssetsFetcher = new AppAssetsFetcher(); exports.appAssetsFetcher = appAssetsFetcher; let RvmMessageBus = require('../rvm_message_bus').rvmMessageBus; let _ = require('underscore'); const moduleTopic_ = 'system'; const moduleAction_ = 'get-rvm-info'; const moduleTimeToLive_ = 5; let validateFetchArguments = function (sourceUrl, successCB, failureCB) { if (typeof sourceUrl !== 'string') { console.log('sourceUrl is required!'); return false; } else if (!successCB) { console.log('successCB is required!'); return false; } else if (!failureCB) { console.log('failureCB is required!'); return false; } else if (!_.isFunction(successCB)) { console.log('successCB must be a function!'); return false; } else if (!_.isFunction(failureCB)) { console.log('failureCB must be a function!'); return false; } return true; }; let isResponseValid = function (dataObj) { let hasAllFields = dataObj && dataObj.topic && dataObj.payload && dataObj.payload.action; if (!hasAllFields) { console.log('Invalid request from the RVM, missing required fields.'); return false; } let topic = dataObj.topic; let payload = dataObj.payload; let action = payload.action; let fieldsAsExpected = (topic === moduleTopic_) && (action === moduleAction_); if (!fieldsAsExpected) { console.log('Invalid request from the RVM, the fields did not containg the correct information.'); return false; } return true; }; let RvmInfoFetcher = function () { let me = this; let pendingRequests_ = []; let handleFailureCases = function (dataObj) { let isFailure = false; let timeToLiveExpired = _.has(dataObj, 'time-to-live-expiration'); if (timeToLiveExpired) { console.log('Time to live of', dataObj['time-to-live-expiration'], 'seconds for rvm info reached.'); dataObj.error = 'Unable to determine rvm information in a reasonable amount of time.'; isFailure = true; } else if (!isResponseValid(dataObj)) { console.log('Received an invalid response from RVM.'); dataObj.error = 'Unable to determine rvm information at this time.'; isFailure = true; } if (isFailure) { pendingRequests_.forEach(request => { request.failureCB(dataObj.error); }); } return isFailure; }; let responseHandler = function (dataObj) { let failureHandled = handleFailureCases(dataObj); if (!failureHandled) { console.log('RvmInfoFetcher received a response from RVM:', dataObj); pendingRequests_.forEach(request => { request.successCB(dataObj.payload); }); } pendingRequests_.splice(0, pendingRequests_.length); }; me.fetch = function (sourceUrl, successCB, failureCB) { let areArgumentsValid = validateFetchArguments(sourceUrl, successCB, failureCB); let isFirstRequester = _.isEmpty(pendingRequests_); if (!areArgumentsValid) { failureCB(new Error('Invalid arguments')); return; } pendingRequests_.push({ successCB: successCB, failureCB: failureCB }); if (isFirstRequester) { let rvmPayload = { topic: moduleTopic_, action: moduleAction_, timeToLive: moduleTimeToLive_, sourceUrl }; if (RvmMessageBus) { RvmMessageBus.publish(rvmPayload, responseHandler); } } }; }; module.exports = new RvmInfoFetcher(); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const transport_1 = require("../transport"); const events_1 = require("events"); const log = require("../log"); const route_1 = require("../../common/route"); const core_state_1 = require("../core_state"); const electron_1 = require("electron"); const _ = require('underscore'); const processVersions = process.versions; class RVMMessageBus extends events_1.EventEmitter { constructor() { super(); this.publish = (msg, callback = () => undefined, maskPayload) => { if (!msg || typeof msg !== 'object') { log.writeToLog('ERROR', 'Argument must be an object'); return false; } const { topic, timeToLive } = msg; const payload = Object.assign({ processId: process.pid, runtimeVersion: processVersions.openfin, securityRealm: core_state_1.argo ? core_state_1.argo['security-realm'] : '' }, msg); delete payload.topic; const envelope = { topic: topic, messageId: electron_1.app.generateGUID(), payload }; this.recordCallbackInfo(callback, timeToLive, envelope); return this.transport.publish(envelope, maskPayload); }; this.registerLicenseInfo = (licInfo, sourceUrl = null) => { const payload = Object.assign({ topic: 'application-event', type: 'started', sourceUrl, sessionId: RVMMessageBus.sessionId, data: { parentApp: { uuid: null }, licenseKey: null, client: { type: null, version: null, pid: null }, uuid: null } }, licInfo); return this.publish(payload); }; this.messageIdToCallback = {}; this.transport = new transport_1.WMCopyData('RvmMessageBus', 'OpenFinRVM_Messaging'); this.transport.on('message', (hwnd, data) => { log.writeToLog(1, `RVMMessageBus: Received message from ${hwnd}`, true); log.writeToLog(1, `RVMMessageBus: ${data}`, true); let dataObj; try { dataObj = JSON.parse(data); if (_.has(dataObj, 'messageId')) { const messageId = dataObj.messageId; const isBroadcastMessage = dataObj.broadcast; const weWereExpectingThisResponse = _.has(this.messageIdToCallback, messageId); if (weWereExpectingThisResponse) { this.messageIdToCallback[messageId](dataObj); delete this.messageIdToCallback[messageId]; } else if (isBroadcastMessage) { const topic = dataObj.topic; const payload = dataObj.payload; const action = dataObj.payload.action; if (topic && payload && action) { this.emit(route_1.default.rvmMessageBus('broadcast', topic, action), payload); } else { log.writeToLog(1, `RVMMessageBus received an invalid broadcast message: ${dataObj}`, true); } } else { log.writeToLog(1, `messageId: ${messageId} has no one waiting for this response, nor was it a broadcast` + 'message, doing nothing.', true); } } else { log.writeToLog(1, 'messageId not found in response.', true); } } catch (e) { log.writeToLog(1, `data must be valid JSON; Error: ${e.message}`, true); } }); } downloadRuntime(options, callback) { const rvmMessage = Object.assign({ topic: 'application', action: 'runtime-download' }, options); const publishSuccess = this.publish(rvmMessage, (response) => { const { payload } = response; if (payload.error) { callback(new Error(payload.error)); } else { callback(); } }); if (!publishSuccess) { callback(new Error('RVM Message failed.')); } } recordCallbackInfo(callback, timeToLiveInSeconds, envelope) { if (callback && _.has(envelope, 'messageId')) { const messageId = envelope.messageId; this.messageIdToCallback[messageId] = callback; if (_.isNumber(timeToLiveInSeconds)) { const timeToLiveInMS = timeToLiveInSeconds * 1000; setTimeout(function () { if (_.has(this.messageIdToCallback, messageId)) { this.messageIdToCallback[messageId]({ 'time-to-live-expiration': timeToLiveInSeconds, envelope }); delete this.messageIdToCallback[messageId]; } }, timeToLiveInMS); } } } sendCloseAppRequested(opts) { return new Promise((resolve, reject) => { try { const rvmMsg = { topic: 'application', action: 'close-app-requested', uuid: opts.uuid, sourceUrl: opts.sourceUrl }; this.publish(rvmMsg, resolve); } catch (err) { reject(err); } }); } sendCloseAppError(opts, err) { return new Promise((resolve, reject) => { try { const rvmMsg = { topic: 'application', action: 'close-app-error', uuid: opts.uuid, sourceUrl: opts.sourceUrl, error: err.message }; this.publish(rvmMsg, resolve); } catch (err) { reject(err); } }); } } RVMMessageBus.sessionId = electron_1.app.generateGUID(); exports.RVMMessageBus = RVMMessageBus; const rvmMessageBus = new RVMMessageBus(); exports.rvmMessageBus = rvmMessageBus; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const rvm_message_bus_1 = require("../rvm/rvm_message_bus"); const system_1 = require("../api/system"); const timers_1 = require("timers"); const maxBytes = 1000000; const defaultFlushInterval = 10000; let consoleMessageQueue = []; let isFlushScheduled = false; let totalBytes = 0; let timer = null; function flushConsoleMessageQueue() { totalBytes = 0; isFlushScheduled = false; if (consoleMessageQueue.length <= 0) { return; } const obj = { topic: 'application', action: 'application-log', sourceUrl: '', runtimeVersion: system_1.System.getVersion(), payload: { messages: JSON.parse(JSON.stringify(consoleMessageQueue)) } }; consoleMessageQueue = []; sendToRVM(obj, true); } function addConsoleMessageToRVMMessageQueue(consoleMessage, flushInterval) { consoleMessageQueue.push(consoleMessage); const byteLength = Buffer.byteLength(consoleMessage.message, 'utf8'); totalBytes += byteLength; if (totalBytes >= maxBytes) { if (timer !== null) { clearTimeout(timer); timer = null; } flushConsoleMessageQueue(); } else if (!isFlushScheduled) { isFlushScheduled = true; timer = timers_1.setTimeout(flushConsoleMessageQueue, flushInterval ? flushInterval : defaultFlushInterval); } } exports.addConsoleMessageToRVMMessageQueue = addConsoleMessageToRVMMessageQueue; function sendToRVM(opts, maskPayload) { return new Promise((resolve, reject) => { if (!rvm_message_bus_1.rvmMessageBus) { return reject(new Error('Connection with RVM is not established')); } const messageSent = rvm_message_bus_1.rvmMessageBus.publish(Object.assign({ timeToLive: 1000 }, opts), (rvmResponse) => { if (!messageSent) { return; } if (typeof rvmResponse !== 'object') { return resolve(rvmResponse); } if (rvmResponse.hasOwnProperty('time-to-live-expiration')) { return reject(new Error('Unable to receive RVM response in a reasonable amount of time')); } if (rvmResponse.success === false) { return reject(new Error(rvmResponse.error)); } if (rvmResponse.payload && rvmResponse.payload.status === false) { return reject(new Error(rvmResponse.payload.error)); } let payload = Object.assign({}, rvmResponse.payload); delete payload.action; delete payload.status; delete payload.error; if (Object.keys(payload).length === 0) { payload = undefined; } resolve(payload); }, maskPayload); if (!messageSent) { reject(new Error('Failed to send a message to the RVM')); } }); } exports.sendToRVM = sendToRVM; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const electron_1 = require("electron"); class Session extends events_1.EventEmitter { constructor() { super(); this.idleState = new electron_1.idleState(); this.idleEventTimer = new electron_1.nativeTimer(() => { this.fireIdleEvent(true, electron_1.app.getTickCount() - this.idleStartTime); }, 60000); this.idleEventTimer.stop(); this.checkIdleStateTimer = new electron_1.nativeTimer(() => { const isIdle = this.idleState.isIdle(); const isTimerRunning = this.idleEventTimer.isRunning(); const timeNow = electron_1.app.getTickCount() - this.idleState.elapsedTime(); if (isIdle && !isTimerRunning) { this.idleStartTime = timeNow; this.fireIdleEvent(true); this.idleEventTimer.reset(); } else if (!isIdle && isTimerRunning) { this.idleEndTime = timeNow; this.fireIdleEvent(false, this.idleEndTime - this.idleStartTime); this.idleEventTimer.stop(); } }, 1000); this.checkIdleStateTimer.reset(); if (process.platform === 'win32') { const bw = new electron_1.BrowserWindow({ show: false }); const WM_WTSSESSION_CHANGE = 0x02B1; bw.hookWindowMessage(WM_WTSSESSION_CHANGE, wParam => { let reason; switch (wParam.readIntLE()) { case 3: reason = 'remote-connect'; break; case 4: reason = 'remote-disconnect'; break; case 7: reason = 'lock'; this.handleLock(true); break; case 8: reason = 'unlock'; this.handleLock(false); break; default: reason = 'unknown'; break; } this.fireSessionEvent(reason); }); bw.subscribeSessionNotifications(true); } else if (process.platform === 'darwin') { electron_1.systemPreferences.subscribeNotification('com.apple.screenIsLocked', () => { this.handleLock(true); this.fireSessionEvent('lock'); }); electron_1.systemPreferences.subscribeNotification('com.apple.screenIsUnlocked', () => { this.handleLock(false); this.fireSessionEvent('unlock'); }); } } fireIdleEvent(isIdle, elapsedTime) { this.emit('idle-state-changed', { elapsedTime: elapsedTime || this.idleState.elapsedTime(), isIdle, topic: 'system', type: 'idle-state-changed' }); } fireSessionEvent(reason) { this.emit('session-changed', { reason, topic: 'system', type: 'session-changed' }); } handleLock(locked) { if (locked) { this.checkIdleStateTimer.stop(); if (!this.idleEventTimer.isRunning()) { this.idleStartTime = electron_1.app.getTickCount(); this.fireIdleEvent(true); this.idleEventTimer.reset(); } } else { this.idleEventTimer.stop(); this.idleEndTime = electron_1.app.getTickCount(); this.fireIdleEvent(false, this.idleEndTime - this.idleStartTime); this.checkIdleStateTimer.reset(); } } } exports.default = new Session(); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const of_events_1 = require("./of_events"); const route_1 = require("../common/route"); class SubscriptionManager { constructor() { this.subscriptionExists = (identity, ...args) => { const key = this.genSubscriptionKey.apply(null, args); const identityKey = this.identityToKey(identity); const identitySubs = this.subscriptionList.get(identityKey); return !!identitySubs && identitySubs.has(key); }; this.uppSubscriptionRefCount = (identity, ...args) => { const key = this.genSubscriptionKey.apply(null, args); const identityKey = this.identityToKey(identity); this.subscriptionList.get(identityKey).get(key).refCount++; }; this.registerSubscription = (fn, identity, ...args) => { const key = this.genSubscriptionKey.apply(null, args); const identityKey = this.identityToKey(identity); if (!this.subscriptionList.has(identityKey)) { this.subscriptionList.set(identityKey, new Map()); } this.subscriptionList.get(identityKey).set(key, { fn, refCount: 1 }); }; this.removeSubscription = (identity, ...args) => { const key = this.genSubscriptionKey.apply(null, args); const identityKey = this.identityToKey(identity); const identitySubs = this.subscriptionList.get(identityKey); const subscription = identitySubs && identitySubs.get(key); if (!subscription) { return; } subscription.refCount -= 1; if (subscription.refCount <= 0) { subscription.fn(); identitySubs.delete(key); } }; this.removeAllSubscriptions = (identity) => { const identityKey = this.identityToKey(identity); const identitySubs = this.subscriptionList.get(identityKey); if (identitySubs) { identitySubs.forEach((sub) => { if (typeof sub.fn === 'function') { sub.fn.call(null); } }); } this.subscriptionList.delete(identityKey); }; this.subscriptionList = new Map(); of_events_1.default.on(route_1.default.window('closed', '*'), (event) => { const identity = event.data[0]; this.removeAllSubscriptions(identity); }); of_events_1.default.on(route_1.default('externalconn', 'closed'), (identity) => { this.removeAllSubscriptions({ uuid: identity.uuid, name: identity.uuid }); }); of_events_1.default.on(route_1.default.frame('disconnected'), (identity) => { this.removeAllSubscriptions(identity); }); } identityToKey(identity) { return encodeURIComponent(`${identity.uuid}/${identity.name}`); } genSubscriptionKey(...args) { const stringArgs = args.reduce((prev, curr) => `${curr}${prev}`); return encodeURIComponent(stringArgs); } } exports.default = SubscriptionManager; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const base_1 = require("./transports/base"); exports.Base = base_1.default; const chromium_ipc_1 = require("./transports/chromium_ipc"); exports.ChromiumIPC = chromium_ipc_1.default; const unix_domain_socket_1 = require("./transports/unix_domain_socket"); exports.UnixDomainSocket = unix_domain_socket_1.default; const wm_copydata_1 = require("./transports/wm_copydata"); exports.WMCopyData = wm_copydata_1.default; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); class MyEmitter extends events_1.EventEmitter { constructor() { super(); } } class BaseTransport { constructor() { this.eventEmitter = new MyEmitter(); } on(eventName, listener) { this.eventEmitter.on.call(this.eventEmitter, eventName, listener); } } exports.default = BaseTransport; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const base_1 = require("./base"); class ChromiumIPCTransport extends base_1.default { constructor(pipeName) { super(); this.pipeName = pipeName; this.messageQueue = []; this.connected = false; this.ipc = new electron_1.chromeIpcClient(); this.ipc.on('channel-error', () => { this.connected = false; this.ipc.close(); this.eventEmitter.emit('ipc-error'); }); this.ipc.on('channel-connected', () => { this.connected = true; this.eventEmitter.emit('ipc-connected'); this.messageQueue.forEach(msg => this.publish(msg)); this.messageQueue.length = 0; }); } connect() { this.connected = false; this.ipc.connect(this.pipeName); } publish(data) { if (this.connected) { this.ipc.send(JSON.stringify(data)); } else { this.messageQueue.push(data); this.connect(); } return true; } } exports.default = ChromiumIPCTransport; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const url_1 = require("url"); const log = require("../log"); const of_events_1 = require("../of_events"); const route_1 = require("../../common/route"); const nodeNet = require("net"); const ProtocolMap = { 'rtmp:': { port: 1935, secureProtocol: 'rtmps:', chromiumProtocol: 'http:' }, 'rtmps:': { port: 443, chromiumProtocol: 'https:' }, 'http:': { port: 80, secureProtocol: 'https:' }, 'https:': { port: 443 } }; var ProxyEventType; (function (ProxyEventType) { ProxyEventType[ProxyEventType["Open"] = 1] = "Open"; ProxyEventType[ProxyEventType["Listening"] = 2] = "Listening"; ProxyEventType[ProxyEventType["ProxyData"] = 3] = "ProxyData"; ProxyEventType[ProxyEventType["ChromiumData"] = 4] = "ChromiumData"; ProxyEventType[ProxyEventType["Closed"] = 5] = "Closed"; })(ProxyEventType || (ProxyEventType = {})); const requestMap = {}; function createChromiumSocket(req) { log.writeToLog(1, `createChromiumSocket: ${JSON.stringify(req)}`, true); createFullProxySocket(req); } exports.createChromiumSocket = createChromiumSocket; function mapUrl(originalUrl) { const mappedUrl = url_1.parse(url_1.format(originalUrl)); if (ProtocolMap.hasOwnProperty(originalUrl.protocol)) { const reqProtocol = ProtocolMap[originalUrl.protocol]; if (reqProtocol.chromiumProtocol) { mappedUrl.protocol = reqProtocol.chromiumProtocol; if (originalUrl.port === '443' && reqProtocol.secureProtocol) { log.writeToLog(1, `applying secure protocol: ${reqProtocol.secureProtocol}`, true); if (ProtocolMap.hasOwnProperty(reqProtocol.secureProtocol)) { mappedUrl.protocol = ProtocolMap[reqProtocol.secureProtocol].chromiumProtocol; } } } } return mappedUrl; } function onFullProxyConnectionEvent(event, request, proxyReq) { if (event.eventType === ProxyEventType.ProxyData) { log.writeToLog(1, `proxy socket output chromium data: ${event.payload.length}`, true); request.writeSocket(event.payload); } else if (event.eventType === ProxyEventType.ChromiumData) { const flushed = event.clientSocket.write(event.payload); log.writeToLog(1, `proxy socket input chromium data: ${event.payload.length} flushed ${flushed}`, true); } else if (event.eventType === ProxyEventType.Closed) { log.writeToLog(1, 'close chromium socket', true); request.closeSocket(); } else if (event.eventType === ProxyEventType.Listening) { proxyReq.callback({ success: true, data: { port: event.port, originalUrl: proxyReq.url } }); } } function createFullProxySocket(req) { const originalUrl = url_1.parse(req.url); const mappedUrl = mapUrl(originalUrl); const url = url_1.format(mappedUrl); const request = electron_1.socketNet.socketRequest(url); requestMap[req.url] = request; request.on('requestSocketConnected', (response) => { log.writeToLog(1, 'requestSocketConnected', true); let clientConn; const server = startProxyConnection((event) => { if (event.eventType === ProxyEventType.Open) { clientConn = event.clientSocket; } onFullProxyConnectionEvent(event, request, req); }); response.on('data', (data) => { onFullProxyConnectionEvent({ eventType: ProxyEventType.ChromiumData, payload: data, clientSocket: clientConn }, request, req); }); response.on('error', (err) => { log.writeToLog(1, `proxy socket response error: ${err}`, true); server.close(); }); }); request.on('socketAuthRequired', (event) => { log.writeToLog(1, `proxy socket auth requested: ${event.url}`, true); of_events_1.default.emit(route_1.default.system('proxy-socket-auth-requested'), { url: event.url, isProxy: event.isProxy }); }); request.on('error', (err) => { log.writeToLog(1, `proxy socket request error ${err}`, true); request.closeSocket(); if (req.errorCallback) { req.errorCallback(err); } }); request.on('close', () => { log.writeToLog(1, `proxy socket request closed, clean up ${req.url}`, true); delete requestMap[req.url]; }); request.createConnection(); } function startProxyConnection(proxyCallback) { let clientConn; const server = nodeNet.createServer((conn) => { if (!clientConn) { log.writeToLog(1, `proxy socket new connection ${conn.localPort}`, true); clientConn = conn; conn.on('data', (data) => { log.writeToLog(1, `proxy socket input data ${data.length}`, true); proxyCallback({ eventType: ProxyEventType.ProxyData, payload: data, clientSocket: conn }); }); conn.on('close', (hadError) => { log.writeToLog(1, `proxy socket closed ${hadError}`, true); server.close(); proxyCallback({ eventType: ProxyEventType.Closed, clientSocket: conn }); }); conn.on('end', () => { log.writeToLog(1, 'proxy socket ended', true); }); conn.on('timeout', () => { log.writeToLog(1, 'proxy socket timeout', true); }); conn.on('error', (err) => { log.writeToLog(1, `proxy socket connect error ${err}`, true); }); proxyCallback({ eventType: ProxyEventType.Open, clientSocket: conn }); } else { log.writeToLog(1, `proxy socket duplicate connection: ${JSON.stringify(server.address())}`, true); conn.end(); } }); server.maxConnections = 1; server.on('listening', () => { log.writeToLog(1, `proxy server info ${JSON.stringify(server.address())}`, true); proxyCallback({ eventType: ProxyEventType.Listening, port: server.address().port }); }); server.on('close', () => { log.writeToLog(1, 'proxy server closed', true); proxyCallback({ eventType: ProxyEventType.Closed }); }); server.on('error', (err) => { log.writeToLog(1, `proxy server error ${err}`, true); server.close(); }); server.listen(0, 'localhost'); return server; } function authenticateChromiumSocket(req) { const request = requestMap[req.url]; if (request) { log.writeToLog(1, `proxy socket auth ${req.url}`, true); request.authenticateSocket(req.username, req.password); } else { log.writeToLog(1, `proxy socket auth missing request ${req.url}`, true); } } exports.authenticateChromiumSocket = authenticateChromiumSocket; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); exports.ipc = electron_1.ipcMain; const channels = { CORE_MESSAGE: 'of-core-message', WINDOW_MESSAGE: 'of-window-message' }; exports.channels = channels; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const http = require('http'); const EventEmitter = require('events').EventEmitter; const util = require('util'); const log = require('../log'); const route_1 = require("../../common/route"); let Server = function () { let me = this; EventEmitter.call(this); let hasStarted = false; let activeConnections = {}; let idPool = require('../int_pool').default; let httpServer = http.createServer(function (req, res) { res.writeHead(403, { 'Content-Type': 'text/plain' }); res.end(''); }); let httpServerError = false; httpServer.on('error', function (err) { httpServerError = true; me.emit(route_1.default.server('error'), err); }); me.getPort = function () { return (httpServer.address() || { port: null }).port; }; me.publish = function (message) { let usedIds = Object.keys(activeConnections); usedIds.forEach(function (id) { if (activeConnections[id]) { activeConnections[id].send(message); } }); }; me.send = function (id, message) { if (activeConnections[id]) { activeConnections[id].send(message); } }; me.closeAllConnections = function () { let usedIds = Object.keys(activeConnections); usedIds.forEach(function (id) { if (activeConnections[id]) { activeConnections[id].close(); } }); }; me.closeConnection = function (id) { if (activeConnections[id]) { activeConnections[id].close(); } }; me.start = function (port) { if (hasStarted && !httpServerError) { log.writeToLog(1, 'socket server already running', true); return; } httpServer.listen(port, '127.0.0.1', function () { if (httpServerError) { httpServerError = false; return; } let WebSocketServer = require('ws').Server, wss = new WebSocketServer({ server: httpServer }); wss.on('headers', function (headers) { me.emit(route_1.default.server('headers'), headers); }); wss.on('error', function (err) { httpServerError = true; me.emit(route_1.default.server('error'), err); }); wss.on('connection', function connection(ws) { let id = idPool.next(); activeConnections[id] = ws; ws.on('error', function (error) { me.emit(route_1.default.connection('error'), id, error); }); ws.on('close', function () { delete activeConnections[id]; idPool.release(id); ws = null; me.emit(route_1.default.connection('close'), id); }); ws.on('open', function () { console.log('Opened ', id); me.emit(route_1.default.connection('open'), id); }); ws.on('message', function incoming(data, flags) { me.emit(route_1.default.connection('message'), id, JSON.parse(data), flags); }); }); me.emit(route_1.default.server('open'), me.getPort()); }); hasStarted = true; }; me.connectionAuthenticated = function (id, uuid) { me.emit(route_1.default.connection('authenticated'), { id, uuid }); }; me.isConnectionOpen = function (id) { const socket = activeConnections[id]; return typeof socket === 'object' && socket.readyState === socket.OPEN; }; }; util.inherits(Server, EventEmitter); module.exports = { server: new Server() }; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const fs_1 = require("fs"); const child_process_1 = require("child_process"); const unixDgram = process.platform === 'win32' ? {} : require('unix-dgram'); const base_1 = require("./base"); const coreState = require("../core_state"); const log = require("../log"); class UnixDomainSocket extends base_1.default { constructor(filenamePrefix) { super(); this.cleanUpServer = () => { log.writeToLog(1, 'Cleaning up unix domain socket transport', true); this.server.close(); fs_1.unlink(this.serverName, (err) => { if (err) { log.writeToLog(1, '[unix domain socket] begin unlink error', true); log.writeToLog(1, err, true); log.writeToLog(1, '[unix domain socket] end unlink error', true); return; } log.writeToLog(1, `${this.serverName} was deleted`, true); }); }; const version = coreState.argo['version-keyword']; const securityRealm = coreState.argo['security-realm'] || ''; this.filenamePrefix = filenamePrefix; this.serverName = `${filenamePrefix}.${version}.${securityRealm ? securityRealm + '.' : ''}${Date.now()}`; this.server = unixDgram.createSocket('unix_dgram', (buffer) => { this.eventEmitter.emit('message', null, buffer); }); this.server.on('listening', () => { log.writeToLog(1, `Now listening on the unix domain socket: ${this.serverName}`, true); }); this.server.bind(this.serverName); electron_1.app.on('quit', this.cleanUpServer); Promise.all([this.getAllFileDescriptors(), this.getOpenFileDescriptors()]).then((values) => { values[0] .filter((fd) => !values[1].includes(fd)) .forEach((fd) => { log.writeToLog(1, `Removing abandoned file descriptor ${fd}`, true); fs_1.unlink(fd, (err) => { if (err) { log.writeToLog(1, '[unix domain socket] begin unlink error', true); log.writeToLog(1, err, true); log.writeToLog(1, '[unix domain socket] end unlink error', true); return; } log.writeToLog(1, `${fd} was deleted`, true); }); }); }); } publish(data) { const message = new Buffer(JSON.stringify(data)); this.getOpenFileDescriptors().then((fds) => { fds .filter((fd) => fd !== this.serverName) .forEach((fd) => { log.writeToLog(1, `Sending a unix domain socket transport message to ${fd}`, true); const client = unixDgram.createSocket('unix_dgram'); client.send(message, 0, message.length, fd); client.close(); }); }); return true; } getAllFileDescriptors() { return new Promise((resolve) => { child_process_1.exec(`/bin/ls ${this.filenamePrefix}*`, (error, stdout) => { if (error) { log.writeToLog(1, '[unix domain socket] begin exec error', true); log.writeToLog(1, error, true); log.writeToLog(1, '[unix domain socket] end exec error', true); return resolve([]); } resolve(this.parseOutput(stdout)); }); }); } getOpenFileDescriptors() { return new Promise((resolve) => { child_process_1.exec(`/usr/sbin/lsof -U | /usr/bin/grep ${this.filenamePrefix}`, (error, stdout) => { if (error) { log.writeToLog(1, '[unix domain socket] begin exec error', true); log.writeToLog(1, error, true); log.writeToLog(1, '[unix domain socket] end exec error', true); return resolve([]); } resolve(this.parseOutput(stdout)); }); }); } parseOutput(output) { return output .split(/\n/) .filter(line => line.length) .map(line => line.split(/\s+/).find(word => word.startsWith(this.filenamePrefix))); } } exports.default = UnixDomainSocket; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const base_1 = require("./base"); const coreState = require("../core_state"); const log = require("../log"); class WMCopyDataTransport extends base_1.default { constructor(senderClass, targetClass) { super(); this.messageRetry = 3; this.senderClass = senderClass; this.targetClass = targetClass; this.initMessageWindow(); } initMessageWindow() { this._messageWindow = new electron_1.MessageWindow(this.senderClass, ''); const msgTimeout = coreState.argo['message-timeout']; if (msgTimeout) { log.writeToLog(1, `${this.senderClass}: set message timeout to ${msgTimeout}`, true); this._messageWindow.setmessagetimeout(msgTimeout); } else { this._messageWindow.setmessagetimeout(1000); } const msgRetry = coreState.argo['message-retry']; if (msgRetry) { log.writeToLog(1, `${this.senderClass}: set message retry to ${msgRetry}`, true); this.messageRetry = msgRetry; } this._messageWindow.on('data', (sender, data) => { this.eventEmitter.emit('message', data.sender, data.message); }); } publish(data, maskPayload) { if (!this._messageWindow || this._messageWindow.isDestroyed()) { this.initMessageWindow(); } let sent = false; let i = 0; for (i = 0; i < this.messageRetry && !sent; i++) { sent = this._messageWindow.sendbyname(this.targetClass, '', JSON.stringify(data), !!maskPayload); if (!sent) { log.writeToLog(1, `${this.senderClass}: error sending message to ${this.targetClass}', retry=${i}`, true); } } return sent; } } exports.default = WMCopyDataTransport; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); function windowSetBoundsToVisible(browserWindow) { const bounds = browserWindow.getBounds(); const { workArea } = electron_1.screen.getDisplayMatching(bounds); const windowIsOutsideOfDisplay = bounds.x > (workArea.x + workArea.width) || (bounds.x + bounds.width) < workArea.x || bounds.y > (workArea.y + workArea.height) || (bounds.y + bounds.height) < workArea.y; if (windowIsOutsideOfDisplay) { browserWindow.setBounds({ x: workArea.x, y: workArea.y, width: bounds.width, height: bounds.height }); } } exports.windowSetBoundsToVisible = windowSetBoundsToVisible; function clipBounds(bounds, browserWindow) { if (!('_options' in browserWindow)) { return bounds; } const { minWidth, minHeight, maxWidth, maxHeight } = browserWindow._options; const xclamp = clamp(bounds.width, minWidth, maxWidth); const yclamp = clamp(bounds.height, minHeight, maxHeight); if (yclamp.clampedOffset || xclamp.clampedOffset) { } return { x: bounds.x + xclamp.clampedOffset, y: bounds.y + yclamp.clampedOffset, width: xclamp.value, height: yclamp.value }; } exports.clipBounds = clipBounds; function clamp(num, min = 0, max = Number.MAX_SAFE_INTEGER) { max = max < 0 ? Number.MAX_SAFE_INTEGER : max; const value = Math.min(Math.max(num, min, 0), max); return { value, clampedOffset: num < min ? -1 * (min - num) : 0 || num > max ? -1 * (num - max) : 0 }; } "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const coreState = require('./core_state'); const electronApp = require('electron').app; const { session, webContents } = require('electron'); const moduleName = 'WebRequestHandlers'; function matchUrlPatterns(url, config) { let match = false; if (config.urlPatterns && config.urlPatterns.length > 0) { match = electronApp.matchesURL(url, config.urlPatterns); } return match; } function applyHeaders(requestHeaders, config) { if (config.headers && config.headers.length > 0) { config.headers.forEach((header) => { Object.keys(header).forEach(key => { requestHeaders[key] = header[key]; electronApp.vlog(1, `${moduleName}:beforeSendHeadersHandler setting ${key} = ${header[key]}`); }); }); } } function beforeSendHeadersHandler(details, callback) { electronApp.vlog(1, `${moduleName}:beforeSendHeadersHandler for ${JSON.stringify(details)}`); let headerAdded = false; if (details.renderProcessId && details.renderFrameId) { const wc = webContents.fromProcessAndFrameIds(details.renderProcessId, details.renderFrameId); if (wc) { electronApp.vlog(1, `${moduleName}:beforeSendHeadersHandler got webcontents ${wc.id}`); const bw = wc.getOwnerBrowserWindow(); if (bw && typeof bw.id === 'number') { const opts = coreState.getWindowOptionsById(bw.id); electronApp.vlog(1, `${moduleName}:beforeSendHeadersHandler window opts ${JSON.stringify(opts)}`); if (opts && opts.customRequestHeaders) { for (const rhItem of opts.customRequestHeaders) { if (matchUrlPatterns(details.url, rhItem)) { applyHeaders(details.requestHeaders, rhItem); headerAdded = true; } } } } } else { electronApp.vlog(1, `${moduleName}:beforeSendHeadersHandler missing webContent`); } } if (headerAdded) { callback({ cancel: false, requestHeaders: details.requestHeaders }); } else { callback({ cancel: false }); } } function initHandlers() { electronApp.vlog(1, `init ${moduleName}`); session.defaultSession.webRequest.onBeforeSendHeaders(beforeSendHeadersHandler); } exports.initHandlers = initHandlers; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); class WindowGroupTransactionTracker extends events_1.EventEmitter { constructor() { super(); this.transactions = {}; } getGroupLeader(groupUuid) { return this.transactions[groupUuid]; } setGroupLeader(groupUuid, name, uuid, type) { this.transactions[groupUuid] = { name, uuid, type }; } clearGroup(groupUuid) { delete this.transactions[groupUuid]; } notifyEndTransaction(groupUuid) { this.emit('end-window-group-transaction', groupUuid); } } exports.default = new WindowGroupTransactionTracker(); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const connection_manager_1 = require("./connection_manager"); const coreState = require("./core_state"); const events_1 = require("events"); const log_1 = require("./log"); const core_state_1 = require("./core_state"); const MIN_API_VER = 37; class MyEmitter extends events_1.EventEmitter { } const externalWindowsProxyList = new Map(); exports.groupProxyEvents = new MyEmitter(); function getWindowKey(identity) { return `${identity.uuid}${identity.name}`; } class RuntimeProxyWindow { constructor(hostRuntime, wrappedWindow, nativeId, windowOptions) { this.registerSingle = async (sourceIdentity) => { if (this.boundLocalWindows.has(getWindowKey(sourceIdentity))) { return this; } this.boundLocalWindows.set(getWindowKey(sourceIdentity), sourceIdentity); await this.remoteMerge(sourceIdentity); return this; }; this.register = async (sourceIdentity) => { this.registerSingle(sourceIdentity); const remoteWindowGroup = await this.getRemoteWindowGroup(); await Promise.all(remoteWindowGroup.map(async (w) => { if (!w.isRegistered) { await w.registerSingle(sourceIdentity); } })); return remoteWindowGroup; }; this.deregister = async (boundIdentity) => { this.boundLocalWindows.delete(getWindowKey(boundIdentity)); }; this.destroy = async () => { const { identity: { uuid, name } } = this.wrappedWindow; const windowKey = `${uuid}${name}`; try { this.window.browserWindow.setExternalWindowNativeId('0x0'); this.window.browserWindow.close(); await this.wrappedWindow.removeListener('group-changed', this.onGroupChanged); } catch (err) { log_1.writeToLog('info', 'Non Fatal error: remove all listeners failed for proxy window'); log_1.writeToLog('info', err); } finally { this.boundLocalWindows.clear(); externalWindowsProxyList.delete(windowKey); } }; this.remoteMerge = async (boundIdentity) => { const source = this.hostRuntime.fin.Window.wrapSync(boundIdentity); await this.wrappedWindow.mergeGroups(source); }; this.wireUpEvents = async () => { await this.wrappedWindow.on('group-changed', this.onGroupChanged); await this.hostRuntime.fin.once('disconnected', () => { this.raiseChangeEvents(this.wrappedWindow.identity, 'remove', []); }); }; this.getRemoteWindowGroup = async () => { const { identity: { uuid, name } } = this.wrappedWindow; const winGroup = await this.wrappedWindow.getGroup(); const existingWindowGroup = []; await Promise.all(winGroup.map(async (w) => { if (coreState.getWindowByUuidName(w.identity.uuid, w.identity.name)) { return; } if (w.identity.uuid !== uuid || w.identity.name !== name) { const pWin = await getRuntimeProxyWindow(w.identity); existingWindowGroup.push(pWin); } })); return existingWindowGroup; }; this.raiseChangeEvents = async (targetIdentity, action, sourceGroup) => { this.boundLocalWindows.forEach(sourceIdentity => { const groupStateChange = { action, targetIdentity, window: this.window, sourceIdentity, sourceGroup }; exports.groupProxyEvents.emit('process-change', groupStateChange); }); }; this.onGroupChanged = (evt) => { log_1.writeToLog('info', 'Group changed event'); log_1.writeToLog('info', evt); if (this.window.uuid === evt.targetWindowAppUuid && this.window.name === evt.targetWindowName) { if (evt.reason === 'leave') { this.raiseChangeEvents({ uuid: evt.targetWindowAppUuid, name: evt.targetWindowName }, 'remove', []); } if (evt.reason === 'join') { this.raiseChangeEvents({ uuid: evt.sourceWindowAppUuid, name: evt.sourceWindowName }, 'add', evt.sourceGroup); } } else if (this.window.uuid === evt.sourceWindowAppUuid && this.window.name === evt.sourceWindowName) { if (evt.reason === 'merge') { this.raiseChangeEvents({ uuid: evt.targetWindowAppUuid, name: evt.targetWindowName }, 'add', []); } } }; const { identity: { uuid, name } } = wrappedWindow; const windowKey = `${uuid}${name}`; const existingProxyWindow = externalWindowsProxyList.get(windowKey); if (existingProxyWindow) { return existingProxyWindow; } this.hostRuntime = hostRuntime; this.wrappedWindow = wrappedWindow; this.boundLocalWindows = new Map(); const proxyWindowOptions = Object.assign({ hwnd: '' + nativeId }, windowOptions); const browserwindow = new electron_1.BrowserWindow(proxyWindowOptions); browserwindow._options = proxyWindowOptions; this.window = { _options: proxyWindowOptions, _window: browserwindow, app_uuid: uuid, browserWindow: browserwindow, children: new Array(), frames: new Map(), forceClose: false, groupUuid: '', hideReason: '', id: 0, name, preloadScripts: new Array(), uuid, mainFrameRoutingId: 0, isProxy: true }; externalWindowsProxyList.set(windowKey, this); } } exports.RuntimeProxyWindow = RuntimeProxyWindow; async function getRuntimeProxyWindow(identity) { const { uuid, name } = identity; const windowKey = `${uuid}${name}`; const existingProxyWindow = externalWindowsProxyList.get(windowKey); if (existingProxyWindow) { return existingProxyWindow; } else { const { runtime: hostRuntime } = await connection_manager_1.default.resolveIdentity(identity); const apiVersion = hostRuntime.portInfo.version.split('.')[2]; if (+apiVersion < MIN_API_VER) { throw new Error(`Window belongs to an older version, cannot group with Windows on version ${hostRuntime.portInfo.version}`); } if (core_state_1.argo['use-legacy-window-groups']) { throw new Error('Unsupported action: Window belongs to another instance of OpenFin and "use-legacy-window-groups" flag suplied.'); } if (hostRuntime.portInfo.options['use-legacy-window-groups']) { throw new Error('Unsupported action: Window belongs to another instance of OpenFin that is using "use-legacy-window-groups".'); } const wrappedWindow = hostRuntime.fin.Window.wrapSync(identity); const nativeId = await wrappedWindow.getNativeId(); const windowOptions = await wrappedWindow.getOptions(); const win = new RuntimeProxyWindow(hostRuntime, wrappedWindow, nativeId, windowOptions); win.wireUpEvents(); return win; } } exports.getRuntimeProxyWindow = getRuntimeProxyWindow; function deregisterAllRuntimeProxyWindows() { externalWindowsProxyList.forEach(runtimeProxyWindow => { runtimeProxyWindow.destroy(); }); } exports.deregisterAllRuntimeProxyWindows = deregisterAllRuntimeProxyWindows; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const crypto_1 = require("crypto"); const _ = require("underscore"); const coreState = require("./core_state"); const windowGroupsProxy = require("./window_groups_runtime_proxy"); const groupTracker = require("./disabled_frame_group_tracker"); const core_state_1 = require("./core_state"); let uuidSeed = 0; class WindowGroups extends events_1.EventEmitter { constructor() { super(); this._windowGroups = {}; this.getGroup = (groupUuid) => { return _.values(this._windowGroups[groupUuid]); }; this.getGroups = () => { return _.map(_.keys(this._windowGroups), (groupUuid) => { return this.getGroup(groupUuid); }); }; this.hasProxyWindows = (groupUuid) => { let hasProxyWindows = false; this.getGroup(groupUuid).forEach(win => { if (win.isProxy) { hasProxyWindows = true; } }); return hasProxyWindows; }; this.getGroupHashName = (groupUuid) => { const winGroup = this.getGroup(groupUuid); const hash = crypto_1.createHash('sha256'); winGroup.map(x => x.browserWindow.nativeId) .sort() .forEach(i => hash.update(i)); return hash.digest('hex'); }; this.getWindowGroupId = (identity) => { const { uuid, name } = identity; return [uuid, name] .map((value) => Buffer.from(value).toString('base64')) .join('/'); }; this.joinGroup = async (source, target) => { const sourceWindow = coreState.getWindowByUuidName(source.uuid, source.name); let targetWindow = coreState.getWindowByUuidName(target.uuid, target.name); let runtimeProxyWindow; const sourceGroupUuid = sourceWindow.groupUuid; if (!targetWindow) { runtimeProxyWindow = await windowGroupsProxy.getRuntimeProxyWindow(target); targetWindow = runtimeProxyWindow.window; } let targetGroupUuid = targetWindow.groupUuid; if (sourceWindow.uuid === targetWindow.uuid && sourceWindow.name === targetWindow.name) { return; } if (sourceGroupUuid && targetGroupUuid && sourceGroupUuid === targetGroupUuid) { return; } if (sourceGroupUuid) { await this.leaveGroup(sourceWindow); } sourceWindow.groupUuid = await this._addWindowToGroup(targetGroupUuid, sourceWindow); if (!targetGroupUuid) { targetWindow.groupUuid = targetGroupUuid = await this._addWindowToGroup(sourceWindow.groupUuid, targetWindow); } if (runtimeProxyWindow) { const windowGroup = await runtimeProxyWindow.register(source); windowGroup.forEach(pWin => this._addWindowToGroup(sourceWindow.groupUuid, pWin.window)); } const payload = generatePayload('join', sourceWindow, targetWindow, this.getGroup(sourceGroupUuid), this.getGroup(targetGroupUuid)); if (sourceGroupUuid) { this.emit('group-changed', { groupUuid: sourceGroupUuid, payload }); } if (targetGroupUuid) { this.emit('group-changed', { groupUuid: targetGroupUuid, payload }); } }; this.leaveGroup = async (win) => { const groupUuid = win && win.groupUuid; if (!groupUuid) { return; } await this._removeWindowFromGroup(groupUuid, win); if (groupUuid) { this.emit('group-changed', { groupUuid, payload: generatePayload('leave', win, win, this.getGroup(groupUuid), []) }); } win.groupUuid = null; if (groupUuid) { await this._handleDisbandingGroup(groupUuid); } }; this.mergeGroups = async (source, target) => { const sourceWindow = coreState.getWindowByUuidName(source.uuid, source.name); let targetWindow = coreState.getWindowByUuidName(target.uuid, target.name); let sourceGroupUuid = sourceWindow.groupUuid; let runtimeProxyWindow; if (!targetWindow) { runtimeProxyWindow = await windowGroupsProxy.getRuntimeProxyWindow(target); targetWindow = runtimeProxyWindow.window; } let targetGroupUuid = targetWindow.groupUuid; if (source === target) { return; } if (sourceGroupUuid && targetGroupUuid && sourceGroupUuid === targetGroupUuid) { return; } if (!targetGroupUuid) { targetWindow.groupUuid = targetGroupUuid = await this._addWindowToGroup(targetGroupUuid, targetWindow); } if (!sourceGroupUuid) { sourceGroupUuid = await this._addWindowToGroup(sourceGroupUuid, sourceWindow); } _.each(this.getGroup(sourceGroupUuid), (win) => { win.groupUuid = targetGroupUuid; }); _.extend(this._windowGroups[targetGroupUuid], this._windowGroups[sourceGroupUuid]); delete this._windowGroups[sourceGroupUuid]; if (runtimeProxyWindow) { const windowGroup = await runtimeProxyWindow.register(source); windowGroup.forEach(pWin => this._addWindowToGroup(sourceWindow.groupUuid, pWin.window)); } const payload = generatePayload('merge', sourceWindow, targetWindow, this.getGroup(sourceGroupUuid), this.getGroup(targetGroupUuid)); if (sourceGroupUuid) { this.emit('group-changed', { groupUuid: sourceGroupUuid, payload }); } if (targetGroupUuid) { this.emit('group-changed', { groupUuid: targetGroupUuid, payload }); } }; this._addWindowToGroup = async (groupUuid, win) => { const windowGroupId = this.getWindowGroupId(win); const _groupUuid = groupUuid || generateUuid(); this._windowGroups[_groupUuid] = this._windowGroups[_groupUuid] || {}; const group = this.getGroup(_groupUuid); this._windowGroups[_groupUuid][windowGroupId] = win; win.groupUuid = _groupUuid; if (!core_state_1.argo['use-legacy-window-groups']) { groupTracker.addWindowToGroup(win); } if (!win.isProxy) { await Promise.all(group.map(async (w) => { if (w.isProxy) { const runtimeProxyWindow = await windowGroupsProxy.getRuntimeProxyWindow(w); await runtimeProxyWindow.registerSingle(win); } })); } return _groupUuid; }; this._removeWindowFromGroup = async (groupUuid, win) => { const windowGroupId = this.getWindowGroupId(win); if (!core_state_1.argo['use-legacy-window-groups']) { groupTracker.removeWindowFromGroup(win); } delete this._windowGroups[groupUuid][windowGroupId]; const group = this.getGroup(groupUuid); await Promise.all(group.map(async (w) => { if (w.isProxy) { const runtimeProxyWindow = await windowGroupsProxy.getRuntimeProxyWindow(w); await runtimeProxyWindow.deregister(win); } })); if (win.isProxy) { const runtimeProxyWindow = await windowGroupsProxy.getRuntimeProxyWindow(win); if (runtimeProxyWindow) { await runtimeProxyWindow.destroy(); } } }; this._handleDisbandingGroup = async (groupUuid) => { const windowGroup = this.getGroup(groupUuid); const windowGroupProxies = windowGroup.filter(w => w.isProxy); if (windowGroup.length < 2 || windowGroup.length === windowGroupProxies.length) { await Promise.all(windowGroup.map(async (win) => { await this._removeWindowFromGroup(groupUuid, win); if (!win.isProxy) { this.emit('group-changed', { groupUuid, payload: generatePayload('disband', win, win, [], []) }); } win.groupUuid = null; })); delete this._windowGroups[groupUuid]; if (!core_state_1.argo['use-legacy-window-groups']) { groupTracker.deleteGroupInfoCache(groupUuid); } } }; windowGroupsProxy.groupProxyEvents.on('process-change', async (changeState) => { if (changeState.action === 'remove') { await this.leaveGroup(changeState.window); } if (changeState.action === 'add') { const runtimeProxyWindow = await windowGroupsProxy.getRuntimeProxyWindow(changeState.targetIdentity); this._addWindowToGroup(changeState.window.groupUuid, runtimeProxyWindow.window); const sourceWindow = coreState.getWindowByUuidName(changeState.sourceIdentity.uuid, changeState.sourceIdentity.name); await runtimeProxyWindow.registerSingle(changeState.sourceIdentity); const sourceGroupUuid = sourceWindow.groupUuid; const payload = generatePayload('join', sourceWindow, runtimeProxyWindow.window, changeState.sourceGroup, this.getGroup(sourceGroupUuid)); if (sourceGroupUuid) { this.emit('group-changed', { groupUuid: sourceGroupUuid, payload }); } } }); } } exports.WindowGroups = WindowGroups; function generateUuid() { return `group${uuidSeed++}`; } function generatePayload(reason, sourceWindow, targetWindow, sourceGroup, targetGroup) { return { reason, sourceGroup: mapEventWindowGroups(sourceGroup), sourceWindowAppUuid: sourceWindow.app_uuid, sourceWindowName: sourceWindow.name, targetGroup: mapEventWindowGroups(targetGroup), targetWindowAppUuid: targetWindow.app_uuid, targetWindowName: targetWindow.name, topic: 'window', type: 'group-changed' }; } function mapEventWindowGroups(group) { return _.map(group, (win) => { return { appUuid: win.app_uuid, windowName: win.name }; }); } exports.default = new WindowGroups(); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const electron_1 = require("electron"); const log_1 = require("../browser/log"); const path = require("path"); const of_events_1 = require("../browser/of_events"); const route_1 = require("./route"); exports.ERROR_TITLE_APP_INITIALIZATION = 'A JavaScript error occured during app initialization'; exports.ERROR_TITLE_MAIN_PROCESS = 'A JavaScript error occurred in the main process'; exports.ERROR_TITLE_RENDERER_CRASH = 'Renderer Crash'; var ERROR_BOX_TYPES; (function (ERROR_BOX_TYPES) { ERROR_BOX_TYPES["APP_INITIALIZATION"] = "OF_error_box:app_initialization"; ERROR_BOX_TYPES["MAIN_PROCESS"] = "OF_error_box:main_process"; ERROR_BOX_TYPES["RENDERER_CRASH"] = "OF_error_box:renderer_crash"; })(ERROR_BOX_TYPES = exports.ERROR_BOX_TYPES || (exports.ERROR_BOX_TYPES = {})); function errorToPOJO(error) { return { stack: error.stack, message: error.message, toString: error.toString }; } exports.errorToPOJO = errorToPOJO; let isInitSafeErrors = false; function initSafeErrors(argo) { if (isInitSafeErrors) { return; } if (!argo['disable-safe-errors']) { process.on('uncaughtException', (error) => { const title = exports.ERROR_TITLE_MAIN_PROCESS; const type = ERROR_BOX_TYPES.MAIN_PROCESS; showErrorBox({ error, title, type }); }); } isInitSafeErrors = true; } exports.initSafeErrors = initSafeErrors; const maxErrorBoxesQty = 10; let errorBoxesQty = 0; function showErrorBox(data) { return new Promise((resolve) => { const { Application } = require('../browser/api/application'); const { argo, deleteApp } = require('../browser/core_state'); const { error, message, title = '', type } = data; const uuid = `error-app-${electron_1.app.generateGUID()}`; const errorMessage = message || error.stack; log_1.writeToLog('info', errorMessage); if (argo.noerrdialogs) { return resolve(); } if (errorBoxesQty >= maxErrorBoxesQty) { log_1.writeToLog('info', 'Not showing custom error box because the ' + 'quantity of active custom error boxes exceeded maximum ' + `allowed of ${maxErrorBoxesQty}`); return; } try { const options = { _type: type, url: `file:///${path.resolve(`${__dirname}/../../assets/error.html`)}`, uuid, name: uuid, mainWindowOptions: { icon: `file:///${path.resolve(`${__dirname}/../../assets/error-icon.png`)}`, defaultHeight: 150, defaultWidth: 570, defaultCentered: true, saveWindowState: false, showTaskbarIcon: false, autoShow: false, alwaysOnTop: true, resizable: false, contextMenu: false, minimizable: false, maximizable: false, nonPersistent: true, experimental: { v2Api: true }, customData: { error: errorMessage, title } } }; Application.create(options); of_events_1.default.once(route_1.default.application('closed', uuid), () => { deleteApp(uuid); errorBoxesQty -= 1; resolve(); }); Application.run({ uuid }); errorBoxesQty += 1; } catch (error) { log_1.writeToLog('info', error); } }); } exports.showErrorBox = showErrorBox; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const url_1 = require("url"); const chromePageWhiteList = [ 'chrome://about', 'chrome://accessibility', 'chrome://appcache-internals', 'chrome://cache', 'chrome://chrome-urls', 'chrome://conflicts', 'chrome://crashes', 'chrome://credits', 'chrome://discards', 'chrome://downloads', 'chrome://extensions', 'chrome://flags', 'chrome://flash', 'chrome://gcm-internals', 'chrome://gpu', 'chrome://histograms', 'chrome://invalidations', 'chrome://media-engagement', 'chrome://nacl', 'chrome://net-export', 'chrome://net-internals', 'chrome://password-manager-internals', 'chrome://policy', 'chrome://print', 'chrome://profiler', 'chrome://quota-internals', 'chrome://serviceworker-internals', 'chrome://site-engagement', 'chrome://system', 'chrome://taskscheduler-internals', 'chrome://tracing', 'chrome://version', 'chrome://view-http-cache', 'chrome://webrtc-internals', 'chrome://inducebrowsercrashforrealz', 'chrome://crash', 'chrome://crashdump', 'chrome://kill', 'chrome://hang', 'chrome://shorthang', 'chrome://gpuclean', 'chrome://gpucrash', 'chrome://gpuhang', 'chrome://memory-exhaust', 'chrome://restart' ]; function isAboutPageUrl(url) { return url && url.startsWith('about:'); } exports.isAboutPageUrl = isAboutPageUrl; function isValidChromePageUrl(url) { return chromePageWhiteList.some(element => url.startsWith(element)); } exports.isValidChromePageUrl = isValidChromePageUrl; function isChromePageUrl(url) { const protocol = url_1.parse(url).protocol || ''; return protocol.startsWith('chrome'); } function isFileUrl(url) { const protocol = url_1.parse(url).protocol || ''; return protocol.startsWith('file'); } exports.isFileUrl = isFileUrl; function isHttpUrl(url) { const protocol = url_1.parse(url).protocol || ''; return protocol.startsWith('http'); } exports.isHttpUrl = isHttpUrl; function isURLAllowed(url) { if (isChromePageUrl(url)) { const { buildFlags } = process; return buildFlags && buildFlags.enableChromium && isValidChromePageUrl(url); } else { return true; } } exports.isURLAllowed = isURLAllowed; function uriToPath(uri) { return uri .replace(/^file:\/\/\/?/, '') .replace(/%20/g, ' '); } exports.uriToPath = uriToPath; exports.getIdentityFromObject = (obj) => { const { uuid, name } = obj; return { uuid, name }; }; function isEnableChromiumBuild() { const { buildFlags } = process; return buildFlags && buildFlags.enableChromium; } exports.isEnableChromiumBuild = isEnableChromiumBuild; function noop() { } exports.noop = noop; function isFloat(n) { return Number(n) === n && n % 1 !== 0; } exports.isFloat = isFloat; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const HYPHEN = { hyphenateUuidName: true }; function router(channel, type, subtopic, subsubtopic, hyphenateUuidName) { let result = `${channel}/${type}`; if (subtopic) { result += `/${subtopic}`; if (subsubtopic) { if (typeof hyphenateUuidName !== 'boolean') { hyphenateUuidName = this && this.hyphenateUuidName; } result += hyphenateUuidName ? '-' : '/'; result += subsubtopic; } } return result; } const route = router.bind(null); route.application = route.bind(null, 'application'); route.externalApplication = route['external-application'] = router.bind(null, 'external-application'); route.frame = router.bind(HYPHEN, 'frame'); route.window = router.bind(HYPHEN, 'window'); route.externalWindow = route['external-window'] = router.bind(HYPHEN, 'external-window'); route.channel = router.bind(HYPHEN, 'channel'); route.system = router.bind(null, 'system'); route.rvmMessageBus = route['rvm-message-bus'] = router.bind(null, 'rvm-message-bus'); route.server = router.bind(null, 'server'); route.connection = router.bind(null, 'connection'); route.runtime = router.bind(null, 'runtime'); route.globalHotkey = route['global-hotkey'] = router.bind(null, 'global-hotkey'); exports.default = route; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function toSafeInt(n, def) { if (Number.isSafeInteger(n)) { return n; } if (typeof (n) === 'number' && Number.isFinite(n)) { return Math.floor(n); } else if (arguments.length >= 2) { try { return toSafeInt(def); } catch (e) { throw new Error(`Neither ${n} nor default value ${def} are parsable numbers.`); } } else { throw new Error(`${n} is not a parsable number and default value not provided.`); } } exports.toSafeInt = toSafeInt; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); class Timer { constructor() { this.reset(); } reset() { this.startTime = Date.now(); } get msecs() { return Date.now() - this.startTime; } get secs() { return this.msecs / 1000; } toString(format = Timer.format) { const match = format.match(/#+(\.#+)?/); let result = this.secs.toFixed(match ? match[1].length - 1 : 0); if (match) { result = format.replace(match[0], result); } return result; } } Timer.format = '#.###'; exports.Timer = Timer; 'use strict'; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); /* global fin, window*/ // These are relative to the preload execution, the root of the proj // THIS FILE GETS EVALED IN THE RENDERER PROCESS (function () { var glbl = global; var QUEUE_COUNTER_NAME = 'queueCounter'; var noteGuidRegex = /^A21B62E0-16B1-4B10-8BE3-BBB6B489D862/; var openfinVersion = process.versions.openfin; var processVersions = JSON.parse(JSON.stringify(process.versions)); var isMainFrame = glbl.isMainFrame; var renderFrameId = glbl.routingId; var customData = glbl.getFrameData(renderFrameId); var electron = require('electron'); // Mock webFrame if unavailable var webFrame = electron.webFrame ? electron.webFrame.createForRenderFrame(renderFrameId) : { getZoomLevel: function getZoomLevel() { return 1.0; }, setZoomLevel: function setZoomLevel() {} }; var ipc = electron.ipcRenderer; var childWindowRequestId = 0; var windowId = void 0; var webContentsId = 0; var _glbl$__startOptions = glbl.__startOptions, elIPCConfig = _glbl$__startOptions.elIPCConfig, enableChromiumBuild = _glbl$__startOptions.enableChromiumBuild, initialOptions = _glbl$__startOptions.options, enableDeprecatedSharedName = _glbl$__startOptions.options.api.iframe.enableDeprecatedSharedName, socketServerState = _glbl$__startOptions.socketServerState, runtimeArguments = _glbl$__startOptions.runtimeArguments, frames = _glbl$__startOptions.frames; //Check if we need to use the process.eval in a nodeless environment. var geval = initialOptions.experimental.node ? glbl.eval : glbl.process.eval; // The following will check whether it is an iframe and update // entity information accordingly var frameInfo = frames.find(function (e) { return e.frameRoutingId === renderFrameId; }); var entityInfo = isMainFrame || enableDeprecatedSharedName ? glbl.__startOptions.entityInfo : frameInfo; var decorateOpen = !runtimeArguments.includes('--native-window-open'); var getOpenerSuccessCallbackCalled = function getOpenerSuccessCallbackCalled() { customData.openerSuccessCalled = customData.openerSuccessCalled || false; return customData.openerSuccessCalled; }; function isNotificationType(name) { var isNotification = noteGuidRegex.test(name); var isQueueCounter = name === QUEUE_COUNTER_NAME; return isNotification || isQueueCounter; } // used by the notification service to emit the ready event function emitNoteProxyReady() { raiseEventSync('notification-service-ready', true); } function asyncApiCall(action) { var payload = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; ipc.send(renderFrameId, 'of-window-message', { action: action, payload: payload, isSync: false, singleFrameOnly: true }); } function syncApiCall(action, payload) { var singleFrameOnly = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; var channel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'of-window-message'; var apiPackage = { action: action, payload: payload, isSync: true, singleFrameOnly: singleFrameOnly }; var responsePayload = JSON.parse(ipc.sendSync(renderFrameId, channel, apiPackage)).payload; if (responsePayload.success) { return responsePayload.data; } else if (responsePayload.error) { var err = new Error(responsePayload.error.message); err.origStack = responsePayload.error.stack; throw err; } else { throw responsePayload.reason; } } function getCachedWindowOptionsSync() { return initialOptions; } function getWindowIdentitySync() { return { uuid: initialOptions.uuid, name: initialOptions.name }; } function getSocketServerStateSync() { return socketServerState; } function convertOptionsToElectronSync(options) { return syncApiCall('convert-options', options); } function windowExistsSync(uuid, name) { return syncApiCall('window-exists', { uuid: uuid, name: name }); } function registerWindowNameSync(uuid, name) { syncApiCall('register-window-name', { uuid: uuid, name: name }); } function getIpcConfigSync() { return elIPCConfig; } function raiseEventSync(eventName, eventArgs) { return syncApiCall('raise-event', { eventName: eventName, eventArgs: eventArgs }); } ///THESE CALLS NEED TO BE DONE WITH REMOTE, AS THEY ARE DONE BEFORE THE CORE HAS ID's function getWindowId() { if (!windowId) { windowId = electron.remote.getCurrentWindow(renderFrameId).id; } return windowId; } function getWebContentsId() { if (!webContentsId) { webContentsId = electron.remote.getCurrentWebContents(renderFrameId).getId(); } return webContentsId; } ////END. function wireUpMouseWheelZoomEvents() { document.addEventListener('mousewheel', function (event) { if (!event.ctrlKey || !initialOptions.accelerator.zoom) { return; } var level = Math.floor(webFrame.getZoomLevel()); webFrame.setZoomLevel(event.wheelDelta >= 0 ? ++level : --level); }); } function wireUpMenu(global) { if (enableChromiumBuild) { return; } global.addEventListener('contextmenu', function (e) { if (!e.defaultPrevented) { e.preventDefault(); var identity = entityInfo.entityType === 'iframe' ? entityInfo.parent : entityInfo; fin.desktop.Window.getCurrent().getOptions(function (options) { if (options.contextMenuSettings.enable) { syncApiCall('show-menu', { uuid: identity.uuid, name: identity.name, editable: e.target.matches('input, textarea, [contenteditable]'), hasSelectedText: e.target.selectionStart !== e.target.selectionEnd, x: e.x, y: e.y }, false); } }); } }); } function disableModifiedClicks(global) { global.addEventListener('auxclick', function (e) { e.preventDefault(); }); global.addEventListener('click', function (e) { var tag = e.target.tagName; var modifiedClick = e.shiftKey || e.metaKey || e.ctrlKey || e.altKey; var mightOpenNewWindow = tag === 'A' || tag === 'IMG'; if (mightOpenNewWindow && modifiedClick) { e.preventDefault(); } else if (modifiedClick && (tag === 'BUTTON' || tag === 'INPUT')) { if (e.target.type === 'submit') { e.preventDefault(); } } }); } function raiseReadyEvents(entityInfo) { var uuid = entityInfo.uuid, name = entityInfo.name, parent = entityInfo.parent, entityType = entityInfo.entityType; var winIdentity = { uuid: uuid, name: name }; var parentFrameName = parent.name || name; var eventMap = []; eventMap.push(['window/initialized/' + uuid + '-' + name, winIdentity]); // main window if (uuid === name) { eventMap.push(['application/initialized/' + uuid, { uuid: uuid }]); } eventMap.push(['window/dom-content-loaded/' + uuid + '-' + name, winIdentity]); eventMap.push(['window/connected/' + uuid + '-' + name, winIdentity]); eventMap.push(['window/frame-connected/' + uuid + '-' + parentFrameName, { frameName: name, entityType: entityType }]); eventMap.push(['frame/connected/' + uuid + '-' + name, winIdentity]); asyncApiCall('raise-many-events', [].concat(eventMap)); } function deferByTick(callback) { setTimeout(function () { if (typeof callback === 'function') { callback.call(window); } }, 1); } var pendingMainCallbacks = []; var currPageHasLoaded = false; global.addEventListener('load', function () { //--------------------------------------------------------------- // TODO: extract this, used to be bound to ready //--------------------------------------------------------------- // The api-ready event allows the webContents to assign api priority. This must happen after // any spin up windowing action or you risk stealing api priority from an already connected frame electron.remote.getCurrentWebContents(renderFrameId).emit('openfin-api-ready', renderFrameId); wireUpMenu(glbl); disableModifiedClicks(glbl); wireUpMouseWheelZoomEvents(); raiseReadyEvents(entityInfo); //TODO:Notifications to be removed from this file. if (/^notification-window/.test(initialOptions.name) && !/^about:blank/.test(location.href)) { fin.desktop.InterApplicationBus.subscribe('*', 'publish-routing-info', function () { fin.desktop.InterApplicationBus.publish('notification-ready', { uuid: initialOptions.uuid, name: initialOptions.name, url: location.href, some: 'other thing', routingInfo: window.payload }); }); fin.desktop.InterApplicationBus.publish('notification-ready', { uuid: initialOptions.uuid, name: initialOptions.name, url: location.href }); } //--------------------------------------------------------------- //--------------------------------------------------------------- currPageHasLoaded = true; if (getOpenerSuccessCallbackCalled() || window.opener === null || initialOptions.rawWindowOpen) { deferByTick(function () { pendingMainCallbacks.forEach(function (callback) { var userAppConfigArgs = initialOptions.userAppConfigArgs; if (userAppConfigArgs) { // handle deep linking callback callback(userAppConfigArgs); } else { callback(); } }); }); } }); function onContentReady(bindObject, callback) { if (currPageHasLoaded && (getOpenerSuccessCallbackCalled() || window.opener === null || initialOptions.rawWindowOpen)) { deferByTick(function () { callback(); }); } else { pendingMainCallbacks.push(callback); } } //extend open var originalOpen = global.open; function openChildWindow() { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } var url = args[0], requestedName = args[1], _args$ = args[2], features = _args$ === undefined ? '' : _args$; // jshint ignore:line var requestId = ++childWindowRequestId; var webContentsId = getWebContentsId(); var name = !windowExistsSync(initialOptions.uuid, requestedName) ? requestedName : fin.desktop.getUuid(); var responseChannel = name + '-created'; var options = Object.assign(featuresToOptionsObj(features), { url: url, uuid: initialOptions.uuid, name: name, autoShow: true }); var convertedOpts = convertOptionsToElectronSync(options); ipc.send(renderFrameId, 'add-child-window-request', responseChannel, name, webContentsId, requestId, JSON.stringify(convertedOpts)); return originalOpen(url, name, features); } //Only decorate global open if flag is not present. if (decorateOpen) { global.open = openChildWindow; } function createChildWindow(options, cb) { var requestId = ++childWindowRequestId; // initialize what's needed to create a child window via window.open var url = (options || {}).url || undefined; var uniqueKey = fin.desktop.getUuid(); var frameName = 'openfin-child-window-' + uniqueKey; //((options || {}).frameName || undefined); var features = (options || {}).features || undefined; var webContentsId = getWebContentsId(); // Reset state machine values that are set through synchronous handshake between native WebContent lifecycle observers and JS options.openfin = true; // Force window to be a child of its parent application. options.uuid = initialOptions.uuid; // Apply parent window background color to child window when child // window background color is unspecified. options.backgroundColor = options.backgroundColor || initialOptions.backgroundColor; var responseChannel = frameName + '-created'; ipc.once(responseChannel, function () { setTimeout(function () { // Synchronous execution of window.open to trigger state tracking of child window var nativeWindow = originalOpen(url !== 'about:blank' ? url : '', frameName, features); var popResponseChannel = frameName + '-pop-request'; ipc.once(popResponseChannel, function (sender, meta) { setTimeout(function () { try { var returnMeta = JSON.parse(meta); cb({ nativeWindow: nativeWindow, id: returnMeta.windowId }); } catch (e) {} }, 1); }); setTimeout(function () { ipc.send(renderFrameId, 'pop-child-window-request', popResponseChannel, frameName, webContentsId, requestId); }, 1); }, 1); }); var convertedOpts = convertOptionsToElectronSync(options); var _ref = 'preloadScripts' in convertedOpts ? convertedOpts : initialOptions, preloadScripts = _ref.preloadScripts; if (!(preloadScripts && preloadScripts.length) || isNotificationType(options.name)) { proceed(); // short-circuit preload scripts fetch } else { var preloadScriptsPayload = { uuid: options.uuid, name: options.name, scripts: preloadScripts }; fin.__internal_.downloadPreloadScripts(preloadScriptsPayload, proceed, proceed); } function proceed() { // PLEASE NOTE: Must stringify options object ipc.send(renderFrameId, 'add-child-window-request', responseChannel, frameName, webContentsId, requestId, JSON.stringify(convertedOpts)); } } global.chrome = global.chrome || {}; global.chrome.desktop = { getDetails: function getDetails(cb) { var details = {}; var currSocketServerState = getSocketServerStateSync(); details.port = currSocketServerState.port; details.ssl = currSocketServerState.isHttps; details.uuid = initialOptions.uuid; details.name = initialOptions.name; details.options = initialOptions; details.versions = processVersions; cb(details); } }; function openerSuccessCBCalled() { customData.openerSuccessCalled = true; deferByTick(function () { pendingMainCallbacks.forEach(function (callback) { callback(); }); }); } //https://developer.mozilla.org/en-US/docs/Web/API/Window/open //All features can be set to yes or 1, or just be present to be "on". Set them to 'no' or 0, or in most cases just omit them, to be "off". function featureToBool() { var value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; switch (value.toLowerCase()) { case '0': return false; case 'no': return false; default: return true; } } //We need to do this as all values are text and convertToElectron does not handle type changes only name translation. function featuresToOptionsObj(features) { var featuresObj = {}; features.split(' ').join('').split(',').map(function (item) { var _item$split = item.split('='), _item$split2 = _slicedToArray(_item$split, 2), name = _item$split2[0], value = _item$split2[1]; switch (name) { /*jshint -W093 */ case 'height': return featuresObj['defaultHeight'] = +value; case 'width': return featuresObj['defaultWidth'] = +value; case 'top': return featuresObj['defaultTop'] = +value; case 'left': return featuresObj['defaultLeft'] = +value; case 'centerscreen': return featuresObj['defaultCentered'] = featureToBool(value); case 'resizable': return featuresObj[name] = featureToBool(value); case 'chrome': return featuresObj['frame'] = featureToBool(value); case 'alwaysRaised': return featuresObj['alwaysOnTop'] = featureToBool(value); case 'minimizable': return featuresObj[name] = featureToBool(value); default: return featuresObj[name] = value; /*jshint +W093 */ } }); return featuresObj; } ///external API Decorator: global.fin = { desktop: { main: function main(cb) { if (typeof cb === 'function') { onContentReady(window, cb); } }, getUuid: process.getGUID, getVersion: function getVersion() { return openfinVersion; } }, __internal_: { ipc: ipc, routingId: renderFrameId, getWindowIdentity: getWindowIdentitySync, getCurrentWindowId: getWindowId, windowExists: windowExistsSync, registerWindowName: registerWindowNameSync, ipcconfig: getIpcConfigSync(), createChildWindow: createChildWindow, getCachedWindowOptionsSync: getCachedWindowOptionsSync, openerSuccessCBCalled: openerSuccessCBCalled, emitNoteProxyReady: emitNoteProxyReady, initialOptions: initialOptions, entityInfo: entityInfo, isMainFrame: isMainFrame } }; /** * An event indicating the moment OpenFin API is injected */ ipc.once('post-api-injection-' + renderFrameId, function () { var uuid = initialOptions.uuid, name = initialOptions.name; if (!isNotificationType(name)) { evalPreloadScripts(uuid, name); } }); /** * zoom event: listen for zoom-in/out keyboard shortcut and messages sent from the browser process using 'send' method */ ipc.on('zoom-' + renderFrameId, function (event, zoom) { if ('level' in zoom) { webFrame.setZoomLevel(zoom.level); } else if ('increment' in zoom) { webFrame.setZoomLevel(zoom.increment ? Math.floor(webFrame.getZoomLevel()) + zoom.increment : 0); } }); /** * Request preload scripts from the Core and execute them in the current window */ function evalPreloadScripts(uuid, name) { var action = 'set-window-preload-state'; var preloadScripts = syncApiCall('get-preload-scripts'); var requiredScriptsFailed = preloadScripts.some(function (e) { var isRequired = true; if (typeof e.mandatory === 'boolean') { isRequired = e.mandatory; } else if (typeof e.optional === 'boolean') { isRequired = !e.optional; // backwards compatibility } return !e._content && isRequired; }); var log = function log(msg) { asyncApiCall('write-to-log', { level: 'info', message: '[preloadScripts] [' + uuid + ']-[' + name + ']: ' + msg }); }; if (requiredScriptsFailed) { // Don't evaluate preload scripts when there // is at least one load-failed that is required log('Aborted execution of preload scripts, ' + 'because at least one required preload script failed to load'); } else { preloadScripts.forEach(function (preloadScript) { // _content: contains preload script code as a string to eval in this window var url = preloadScript.url, _content = preloadScript._content; if (!_content) { log('Skipped execution of preload script for URL [' + url + '], ' + 'because the content is not available'); return; } try { geval(_content); /* jshint ignore:line */ log('Succeeded execution of preload script for URL [' + url + ']'); asyncApiCall(action, { url: url, state: 'succeeded' }); } catch (error) { window.console.error(error.name + ': ' + error.message + '\nPreload script: ' + url); log('Failed execution of preload script for URL [' + url + ']: ' + error); asyncApiCall(action, { url: url, state: 'failed' }); } }); } asyncApiCall(action, { allDone: true }); } })(); 'use strict'; module.exports = process.binding('atom_common_asar'); 'use strict'; var extend = require('util')._extend; var fs = require('fs'); var extendedFs = extend(fs, { internalModuleStat: process.binding('fs').internalModuleStat, internalModuleReadFile: process.binding('fs').internalModuleReadFile }); module.exports = extendedFs; "use strict"; var proc = process; module.exports = proc; 'use strict'; var fs = require('fs'); var path = require('path'); var coreState = require('../browser/core_state.js'); function readAdapterFromSearchPaths(searchPaths, packageFile) { var adapter = ''; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = searchPaths[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var adapterPath = _step.value; try { adapter = fs.readFileSync(path.join(process.resourcesPath, adapterPath, packageFile), 'utf8'); break; } catch (error) { continue; } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return adapter; } // check resources/adapter/openfin-desktop.js then // resources/adapter.asar/openfin-desktop.js // for ease of developement var searchPaths = ['adapter', 'adapter.asar']; var jsAdapter = readAdapterFromSearchPaths(searchPaths, 'openfin-desktop.js'); // This order of lookup paths is taken from runtime/lib/browser/init.js var searchPathsV2Api = ['default_app', 'core', 'app', 'core.asar', 'app.asar', 'default_app.asar'].map(function (e) { return path.join(e, 'js-adapter'); }); var jsAdapterV2 = readAdapterFromSearchPaths(searchPathsV2Api, 'js-adapter.js'); // Remove strict (Prevents, as of now, poorly understood memory lifetime scoping issues with remote module) var me = fs.readFileSync(path.join(__dirname, 'api-decorator.js'), 'utf8'); me = me.slice(13); var api = function api(windowId, initialOptions) { var windowOptionSet = initialOptions || coreState.getWindowInitialOptionSet(windowId); var mainWindowOptions = windowOptionSet.options || {}; var enableV2Api = (mainWindowOptions.experimental || {}).v2Api; var v2AdapterShim = !enableV2Api ? '' : jsAdapterV2; windowOptionSet.runtimeArguments = JSON.stringify(coreState.args); return ['global.__startOptions = ' + JSON.stringify(windowOptionSet), me, jsAdapter, v2AdapterShim, 'fin.__internal_.ipc = null'].join(';'); }; module.exports.api = api; module.exports.apiWithOptions = function (windowId) { var initialOptions = coreState.getWindowInitialOptionSet(windowId); // break the remote link return JSON.stringify({ apiString: api(windowId, initialOptions), initialOptions: initialOptions }); }; 'use strict'; 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); } } /* global routingId, isMainFrame, isSameOriginIframe, isCrossOriginIframe, isChildMainFrame */ var electron = require('electron'); var resolvePromise = Promise.resolve.bind(Promise); var defaultWebFrame = electron.webFrame; var susbcribeForTeardown = function susbcribeForTeardown(routingId) { var handlers = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; process.once('frame-exit-' + routingId, function () { handlers.forEach(function (teardownHandler) { if (typeof teardownHandler === 'function') { try { teardownHandler(); } catch (error) { console.error('Error cleaning up renderFrame ' + routingId); console.error(error.stack); } } }); }); }; // OpenFin: these values are used in lib/common/api/crash-reporter.js process.versions = process.versions || {}; process.versions.combinedId = electron.remote.app.getCombinedId(); process.versions.openfin = electron.remote.app.getRuntimeVersion(); process.versions.mainFrameRoutingId = electron.ipcRenderer.getFrameRoutingID(); process.versions.cachePath = electron.remote.app.getPath('userData'); var mainWindowId = electron.remote.getCurrentWindow(process.versions.mainFrameRoutingId).id; var apiInfo = electron.remote.require('./src/renderer/main').apiWithOptions(mainWindowId); var _JSON$parse = JSON.parse(apiInfo), apiString = _JSON$parse.apiString, initialOptions = _JSON$parse.initialOptions; // let chromiumWindowAlertEnabled = electron.remote.app.getCommandLineArguments().includes('--enable-chromium-window-alert'); var hookWebFrame = function hookWebFrame(webFrame, renderFrameId) { electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD-' + renderFrameId, function (event, method, args) { webFrame[method].apply(webFrame, _toConsumableArray(args)); }); electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD-' + renderFrameId, function (event, requestId, method, args) { var result = webFrame[method].apply(webFrame, _toConsumableArray(args)); event.sender.send(renderFrameId, 'ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_' + requestId, result); }); electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD-' + renderFrameId, function (event, requestId, method, args) { var responseCallback = function responseCallback(result) { resolvePromise(result).then(function (resolvedResult) { event.sender.send(renderFrameId, 'ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_' + requestId, null, resolvedResult); }).catch(function (resolvedError) { event.sender.send(renderFrameId, 'ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_' + requestId, resolvedError); }); }; args.push(responseCallback); webFrame[method].apply(webFrame, _toConsumableArray(args)); }); // Teardown return function () { electron.ipcRenderer.removeAllListeners('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD-' + renderFrameId); electron.ipcRenderer.removeAllListeners('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD-' + renderFrameId); electron.ipcRenderer.removeAllListeners('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD-' + renderFrameId); }; }; // Handle spin-up and tear-down var registerAPI = function registerAPI(w, routingId, isMainFrame, isSameOriginIframe, isCrossOriginIframe, isChildMainFrame) { var teardownHandlers = []; teardownHandlers.push(hookWebFrame(defaultWebFrame.createForRenderFrame(routingId), routingId)); try { if (window.location.protocol === 'chrome-devtools:') { return; } // Mock as a Node/Electron environment // =================================== w.getFrameData = process.getFrameData; w.require = require; w.global = global; w.process = process; w.routingId = routingId || electron.ipcRenderer.getFrameRoutingID(); w.isMainFrame = isMainFrame; // =================================== var _initialOptions$optio = initialOptions.options.api.iframe, sameOriginInjection = _initialOptions$optio.sameOriginInjection, crossOriginInjection = _initialOptions$optio.crossOriginInjection; var inboundMessageTopic = ''; var apiInjectionAllowed = isMainFrame || isChildMainFrame || isSameOriginIframe && sameOriginInjection || isCrossOriginIframe && crossOriginInjection; if (apiInjectionAllowed) { w.process.eval(apiString); if (w.fin) { inboundMessageTopic = w.fin.__internal_.ipcconfig.channels.CORE_MESSAGE + '-' + w.fin.__internal_.routingId; } else { console.warn('failed to load OpenFin api'); } } delete w.require; delete w.process; delete w.module; delete w.Buffer; delete w.routingId; delete w.isMainFrame; delete w.global; delete w.getFrameData; w = undefined; try { electron.ipcRenderer.emit('post-api-injection', routingId); electron.ipcRenderer.emit('post-api-injection-' + routingId); } catch (error) { console.error('Error notifying post-api-injection for ' + routingId); console.error(error.stack); } finally { electron.ipcRenderer.removeAllListeners('post-api-injection-' + routingId); } if (inboundMessageTopic.length) { teardownHandlers.push(function () { try { electron.ipcRenderer.emit('teardown-render-frame', routingId); electron.ipcRenderer.emit('teardown-render-frame-' + routingId); electron.ipcRenderer.removeAllListeners(inboundMessageTopic); // Fallback last-resort teardown for adapter owned topics. electron.ipcRenderer.removeAllListeners('teardown-render-frame-' + routingId); electron.ipcRenderer.removeAllListeners('zoom-' + routingId); } catch (error) { console.error('Error cleaning up renderFrame ' + routingId); console.error(error.stack); } }); } } catch (error) { // w.debugMessages.push('errrrr'); console.error(error); console.error(error.stack); electron.ipcRenderer.send(routingId, 'api-injection-failed', routingId); } susbcribeForTeardown(routingId, teardownHandlers); }; registerAPI(window, routingId, isMainFrame, isSameOriginIframe, isCrossOriginIframe, isChildMainFrame, initialOptions); "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DEFAULT_RESIZE_REGION_SIZE = 7; exports.DEFAULT_RESIZE_REGION_BOTTOM_RIGHT_CORNER = 9; exports.DEFAULT_RESIZE_SIDES = { top: true, right: true, bottom: true, left: true };