@jsenv/plugin-toolbar 1.4.3 → 1.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -798,69 +798,141 @@ E(() => {
798
798
  }
799
799
  });
800
800
 
801
- const sendEventToParent = (name, data) => {
802
- window.parent.postMessage({
803
- __jsenv__: {
804
- event: name,
805
- data
801
+ const changesTooltipOpenedSignal = d$1(false);
802
+
803
+ const openChangesToolip = () => {
804
+ changesTooltipOpenedSignal.value = true;
805
+ };
806
+ const closeChangesToolip = () => {
807
+ changesTooltipOpenedSignal.value = false;
808
+ };
809
+
810
+ const autoreloadEnabledSignal = d$1(false);
811
+ const reloaderStatusSignal = d$1("idle");
812
+ const changesSignal = d$1(0);
813
+ if (parentWindowReloader) {
814
+ autoreloadEnabledSignal.value = parentWindowReloader.autoreload.enabled;
815
+ parentWindowReloader.autoreload.onchange = () => {
816
+ autoreloadEnabledSignal.value = parentWindowReloader.autoreload.enabled;
817
+ };
818
+ reloaderStatusSignal.value = parentWindowReloader.status.value;
819
+ const onchange = parentWindowReloader.status.onchange;
820
+ parentWindowReloader.status.onchange = () => {
821
+ onchange();
822
+ reloaderStatusSignal.value = parentWindowReloader.status.value;
823
+ };
824
+ changesSignal.value = parentWindowReloader.changes.value;
825
+ parentWindowReloader.changes.onchange = () => {
826
+ changesSignal.value = [...parentWindowReloader.changes.value];
827
+ };
828
+ }
829
+
830
+ const enableVariant = (rootNode, variables) => {
831
+ const nodesNotMatching = Array.from(rootNode.querySelectorAll("[".concat(attributeIndicatingACondition, "]")));
832
+ nodesNotMatching.forEach(nodeNotMatching => {
833
+ const conditionAttributeValue = nodeNotMatching.getAttribute(attributeIndicatingACondition);
834
+ const matches = testCondition(conditionAttributeValue, variables);
835
+ if (matches) {
836
+ renameAttribute(nodeNotMatching, attributeIndicatingACondition, attributeIndicatingAMatch);
806
837
  }
807
- }, "*");
838
+ });
839
+ const nodesMatching = Array.from(rootNode.querySelectorAll("[".concat(attributeIndicatingAMatch, "]")));
840
+ nodesMatching.forEach(nodeMatching => {
841
+ const conditionAttributeValue = nodeMatching.getAttribute(attributeIndicatingAMatch);
842
+ const matches = testCondition(conditionAttributeValue, variables);
843
+ if (!matches) {
844
+ renameAttribute(nodeMatching, attributeIndicatingAMatch, attributeIndicatingACondition);
845
+ }
846
+ });
808
847
  };
809
- const addExternalCommandCallback = (command, callback) => {
810
- const messageEventCallback = messageEvent => {
811
- const {
812
- data
813
- } = messageEvent;
814
- if (typeof data !== "object") {
815
- return;
848
+ const testCondition = (conditionAttributeValue, variables) => {
849
+ const condition = parseCondition(conditionAttributeValue);
850
+ return Object.keys(variables).some(key => {
851
+ if (condition.key !== key) {
852
+ return false;
816
853
  }
817
- const {
818
- __jsenv__
819
- } = data;
820
- if (!__jsenv__) {
821
- return;
854
+ // the condition do not specify a value, any value is ok
855
+ if (condition.value === undefined) {
856
+ return true;
822
857
  }
823
- if (__jsenv__.command !== command) {
824
- return;
858
+ if (condition.value === variables[key]) {
859
+ return true;
825
860
  }
826
- callback(...__jsenv__.args);
827
- };
828
- window.addEventListener("message", messageEventCallback);
829
- return () => {
830
- window.removeEventListener("message", messageEventCallback);
831
- };
861
+ return false;
862
+ });
832
863
  };
833
- const enableAutoreload = () => {
834
- parentWindowReloader.autoreload.enable();
864
+ const parseCondition = conditionAttributeValue => {
865
+ const colonIndex = conditionAttributeValue.indexOf(":");
866
+ if (colonIndex === -1) {
867
+ return {
868
+ key: conditionAttributeValue,
869
+ value: undefined
870
+ };
871
+ }
872
+ return {
873
+ key: conditionAttributeValue.slice(0, colonIndex),
874
+ value: conditionAttributeValue.slice(colonIndex + 1)
875
+ };
835
876
  };
836
- const disableAutoreload = () => {
837
- parentWindowReloader.autoreload.disable();
877
+ const attributeIndicatingACondition = "data-when";
878
+ const attributeIndicatingAMatch = "data-when-active";
879
+ const renameAttribute = (node, name, newName) => {
880
+ node.setAttribute(newName, node.getAttribute(name));
881
+ node.removeAttribute(name);
838
882
  };
839
883
 
840
- const serverTooltipOpenedSignal = d$1(false);
841
- const serverConnectionSignal = d$1("default");
842
- const serverEvents$1 = window.__server_events__;
843
- if (serverEvents$1) {
844
- serverEvents$1.readyState.onchange = () => {
845
- serverConnectionSignal.value = serverEvents$1.readyState.value;
884
+ const changesIndicator = document.querySelector("#changes_indicator");
885
+ const renderChangesIndicator = () => {
886
+ E(() => {
887
+ const autoreloadEnabled = autoreloadEnabledSignal.value;
888
+ const changes = changesSignal.value;
889
+ const changeCount = changes.length;
890
+ enableVariant(changesIndicator, {
891
+ changes: !autoreloadEnabled && changeCount ? "yes" : "no"
892
+ });
893
+ if (changeCount) {
894
+ changesIndicator.querySelector(".tooltip_text").innerHTML = computeTooltipText({
895
+ changes
896
+ });
897
+ changesIndicator.querySelector(".tooltip_text a").onclick = () => {
898
+ // eslint-disable-next-line no-alert
899
+ window.alert(JSON.stringify(changes, null, " "));
900
+ console.log(changes);
901
+ };
902
+ changesIndicator.querySelector(".changes_text").innerHTML = changeCount;
903
+ }
904
+ });
905
+ changesIndicator.querySelector(".tooltip_action").onclick = () => {
906
+ parentWindowReloader.reload();
907
+ };
908
+ E(() => {
909
+ const changesTooltipOpened = changesTooltipOpenedSignal.value;
910
+ if (changesTooltipOpened) {
911
+ changesIndicator.setAttribute("data-tooltip-visible", "");
912
+ } else {
913
+ changesIndicator.removeAttribute("data-tooltip-visible");
914
+ }
915
+ });
916
+ const button = changesIndicator.querySelector("button");
917
+ button.onclick = () => {
918
+ const changesTooltipOpened = changesTooltipOpenedSignal.value;
919
+ if (changesTooltipOpened) {
920
+ closeChangesToolip();
921
+ } else {
922
+ openChangesToolip();
923
+ }
846
924
  };
847
- serverConnectionSignal.value = serverEvents$1.readyState.value;
848
- }
849
-
850
- const openServerTooltip = () => {
851
- serverTooltipOpenedSignal.value = true;
852
- };
853
- const closeServerTooltip = () => {
854
- serverTooltipOpenedSignal.value = false;
855
925
  };
856
-
857
- const closeAllTooltips = () => {
858
- closeExecutionTooltip();
859
- closeServerTooltip();
926
+ const computeTooltipText = ({
927
+ changes
928
+ }) => {
929
+ const changesCount = changes.length;
930
+ if (changesCount === 1) {
931
+ return "There is <a href=\"javascript:void(0)\">1</a> change to apply";
932
+ }
933
+ return "There is <a href=\"javascript:void(0)\">".concat(changesCount, "<a> changes to apply");
860
934
  };
861
935
 
862
- const openedSignal = d$1(typeof stateFromLocalStorage.opened === "boolean" ? stateFromLocalStorage.opened : typeof paramsFromParentWindow.opened === "boolean" ? paramsFromParentWindow.opened : false);
863
-
864
936
  const getToolbarIframe = () => {
865
937
  const iframes = Array.from(window.parent.document.querySelectorAll("iframe"));
866
938
  return iframes.find(iframe => iframe.contentWindow === window);
@@ -899,116 +971,171 @@ const deactivateToolbarSection = element => {
899
971
  element.removeAttribute("data-active");
900
972
  };
901
973
 
902
- const startJavaScriptAnimation = ({
903
- duration = 300,
904
- timingFunction = t => t,
905
- onProgress = () => {},
906
- onCancel = () => {},
907
- onComplete = () => {}
908
- }) => {
909
- if (isNaN(duration)) {
910
- // console.warn(`duration must be a number, received ${duration}`)
911
- return () => {};
912
- }
913
- duration = parseInt(duration, 10);
914
- const startMs = performance.now();
915
- let currentRequestAnimationFrameId;
916
- let done = false;
917
- let rawProgress = 0;
918
- let progress = 0;
919
- const handler = () => {
920
- currentRequestAnimationFrameId = null;
921
- const nowMs = performance.now();
922
- rawProgress = Math.min((nowMs - startMs) / duration, 1);
923
- progress = timingFunction(rawProgress);
924
- done = rawProgress === 1;
925
- onProgress({
926
- done,
927
- rawProgress,
928
- progress
929
- });
930
- if (done) {
931
- onComplete();
974
+ const executionIndicator = document.querySelector("#document_execution_indicator");
975
+ const renderDocumentExecutionIndicator = async () => {
976
+ removeForceHideElement(document.querySelector("#document_execution_indicator"));
977
+ E(() => {
978
+ const execution = executionSignal.value;
979
+ updateExecutionIndicator(execution);
980
+ });
981
+ E(() => {
982
+ const executionTooltipOpened = executionTooltipOpenedSignal.value;
983
+ if (executionTooltipOpened) {
984
+ executionIndicator.setAttribute("data-tooltip-visible", "");
932
985
  } else {
933
- currentRequestAnimationFrameId = window.requestAnimationFrame(handler);
934
- }
935
- };
936
- handler();
937
- const stop = () => {
938
- if (currentRequestAnimationFrameId) {
939
- window.cancelAnimationFrame(currentRequestAnimationFrameId);
940
- currentRequestAnimationFrameId = null;
986
+ executionIndicator.removeAttribute("data-tooltip-visible");
941
987
  }
942
- if (!done) {
943
- done = true;
944
- onCancel({
945
- rawProgress,
946
- progress
947
- });
988
+ });
989
+ };
990
+ const updateExecutionIndicator = ({
991
+ status,
992
+ startTime,
993
+ endTime
994
+ } = {}) => {
995
+ enableVariant(executionIndicator, {
996
+ execution: status
997
+ });
998
+ const variantNode = executionIndicator.querySelector("[data-when-active]");
999
+ variantNode.querySelector("button").onclick = () => {
1000
+ const executionTooltipOpened = executionTooltipOpenedSignal.value;
1001
+ if (executionTooltipOpened) {
1002
+ closeExecutionTooltip();
1003
+ } else {
1004
+ openExecutionTooltip();
948
1005
  }
949
1006
  };
950
- return stop;
1007
+ variantNode.querySelector(".tooltip").textContent = computeText({
1008
+ status,
1009
+ startTime,
1010
+ endTime
1011
+ });
951
1012
  };
952
1013
 
953
- const initToolbarOpening = () => {
954
- E(() => {
955
- const opened = openedSignal.value;
956
- if (opened) {
957
- showToolbar();
958
- } else {
959
- hideToolbar();
1014
+ // relative time: https://github.com/tc39/proposal-intl-relative-time/issues/118
1015
+ const computeText = ({
1016
+ status,
1017
+ startTime,
1018
+ endTime
1019
+ }) => {
1020
+ if (status === "completed") {
1021
+ return "Execution completed in ".concat(endTime - startTime, "ms");
1022
+ }
1023
+ if (status === "failed") {
1024
+ return "Execution failed in ".concat(endTime - startTime, "ms");
1025
+ }
1026
+ if (status === "running") {
1027
+ return "Executing...";
1028
+ }
1029
+ return "";
1030
+ };
1031
+
1032
+ const setLinkHrefForParentWindow = (a, href) => {
1033
+ a.href = href;
1034
+ a.onclick = e => {
1035
+ if (e.ctrlKey || e.metaKey) {
1036
+ return;
960
1037
  }
961
- });
1038
+ e.preventDefault();
1039
+ window.parent.location.href = href;
1040
+ };
962
1041
  };
963
- let restoreToolbarIframeParentStyles = () => {};
964
- let restoreToolbarIframeStyles = () => {};
965
- const hideToolbar = () => {
966
- closeAllTooltips();
967
- restoreToolbarIframeParentStyles();
968
- restoreToolbarIframeStyles();
969
- document.documentElement.removeAttribute("data-toolbar-visible");
1042
+
1043
+ const renderDocumentIndexLink = () => {
1044
+ setLinkHrefForParentWindow(document.querySelector("#document_index_link"), "/");
970
1045
  };
971
1046
 
972
- // (by the way it might be cool to have the toolbar auto show when)
973
- // it has something to say (being disconnected from server)
974
- const showToolbar = () => {
975
- const animationsEnabled = animationsEnabledSignal.peek();
976
- document.documentElement.setAttribute("data-toolbar-visible", "");
977
- const toolbarIframe = getToolbarIframe();
978
- const toolbarIframeParent = toolbarIframe.parentNode;
979
- const parentWindow = window.parent;
980
- const parentDocumentElement = parentWindow.document.compatMode === "CSS1Compat" ? parentWindow.document.documentElement : parentWindow.document.body;
981
- const scrollYMax = parentDocumentElement.scrollHeight - parentWindow.innerHeight;
982
- const scrollY = parentDocumentElement.scrollTop;
983
- const scrollYRemaining = scrollYMax - scrollY;
984
- setStyles(toolbarIframeParent, {
985
- "transition-property": "padding-bottom",
986
- "transition-duration": animationsEnabled ? "300ms" : "0s"
1047
+ const serverTooltipOpenedSignal = d$1(false);
1048
+ const serverConnectionSignal = d$1("default");
1049
+ const serverEvents$1 = window.__server_events__;
1050
+ if (serverEvents$1) {
1051
+ serverEvents$1.readyState.onchange = () => {
1052
+ serverConnectionSignal.value = serverEvents$1.readyState.value;
1053
+ };
1054
+ serverConnectionSignal.value = serverEvents$1.readyState.value;
1055
+ }
1056
+
1057
+ const openServerTooltip = () => {
1058
+ serverTooltipOpenedSignal.value = true;
1059
+ };
1060
+ const closeServerTooltip = () => {
1061
+ serverTooltipOpenedSignal.value = false;
1062
+ };
1063
+
1064
+ const parentServerEvents = window.parent.__server_events__;
1065
+ const serverEvents = window.__server_events__;
1066
+ const serverIndicator = document.querySelector("#server_indicator");
1067
+ const renderServerIndicator = () => {
1068
+ removeForceHideElement(document.querySelector("#server_indicator"));
1069
+ E(() => {
1070
+ const serverConnection = serverConnectionSignal.value;
1071
+ updateServerIndicator(serverConnection);
987
1072
  });
988
- // maybe we should use js animation here because we would not conflict with css
989
- restoreToolbarIframeParentStyles = setStyles(toolbarIframeParent, {
990
- "scroll-padding-bottom": "40px",
991
- // same here we should add 40px
992
- "padding-bottom": "40px" // if there is already one we should add 40px
1073
+ E(() => {
1074
+ const serverTooltipOpened = serverTooltipOpenedSignal.value;
1075
+ if (serverTooltipOpened) {
1076
+ serverIndicator.setAttribute("data-tooltip-visible", "");
1077
+ } else {
1078
+ serverIndicator.removeAttribute("data-tooltip-visible");
1079
+ }
993
1080
  });
994
- restoreToolbarIframeStyles = setStyles(toolbarIframe, {
995
- height: "40px",
996
- visibility: "visible"
1081
+ };
1082
+ const updateServerIndicator = connectionState => {
1083
+ enableVariant(serverIndicator, {
1084
+ connectionState
997
1085
  });
998
- if (scrollYRemaining < 40 && scrollYMax > 0) {
999
- const scrollEnd = scrollY + 40;
1000
- startJavaScriptAnimation({
1001
- duration: 300,
1002
- onProgress: ({
1003
- progress
1004
- }) => {
1005
- const value = scrollY + (scrollEnd - scrollY) * progress;
1006
- parentDocumentElement.scrollTop = value;
1086
+ const variantNode = document.querySelector("#server_indicator > [data-when-active]");
1087
+ variantNode.querySelector("button").onclick = () => {
1088
+ const serverTooltipOpened = serverTooltipOpenedSignal.value;
1089
+ if (serverTooltipOpened) {
1090
+ closeServerTooltip();
1091
+ } else {
1092
+ openServerTooltip();
1093
+ }
1094
+ };
1095
+ if (connectionState === "connecting") {
1096
+ variantNode.querySelector("a").onclick = () => {
1097
+ if (parentServerEvents) {
1098
+ parentServerEvents.disconnect();
1007
1099
  }
1008
- });
1100
+ serverEvents.disconnect();
1101
+ };
1102
+ } else if (connectionState === "closed") {
1103
+ variantNode.querySelector("a").onclick = () => {
1104
+ if (parentServerEvents) {
1105
+ parentServerEvents.connect();
1106
+ }
1107
+ serverEvents.connect();
1108
+ };
1009
1109
  }
1010
1110
  };
1011
1111
 
1112
+ const openedSignal = d$1(typeof stateFromLocalStorage.opened === "boolean" ? stateFromLocalStorage.opened : typeof paramsFromParentWindow.opened === "boolean" ? paramsFromParentWindow.opened : false);
1113
+
1114
+ const openToolbar = () => {
1115
+ openedSignal.value = true;
1116
+ };
1117
+ const closeToolbar = () => {
1118
+ openedSignal.value = false;
1119
+ };
1120
+
1121
+ const renderToolbarCloseButton = () => {
1122
+ // if user click enter or space quickly while closing toolbar
1123
+ // it will cancel the closing
1124
+ // that's why I used toggleToolbar and not hideToolbar
1125
+ document.querySelector("#toolbar_close_button").onclick = () => {
1126
+ if (openedSignal.value) {
1127
+ closeToolbar();
1128
+ } else {
1129
+ openToolbar();
1130
+ }
1131
+ };
1132
+ };
1133
+
1134
+ const closeAllTooltips = () => {
1135
+ closeExecutionTooltip();
1136
+ closeServerTooltip();
1137
+ };
1138
+
1012
1139
  const createHorizontalBreakpoint = breakpointValue => {
1013
1140
  return createBreakpoint(windowWidthMeasure, breakpointValue);
1014
1141
  };
@@ -1193,330 +1320,249 @@ const closeOverflowMenu = () => {
1193
1320
  document.querySelector("#menu_overflow").removeAttribute("data-animate");
1194
1321
  };
1195
1322
 
1196
- const settingsOpenedSignal = d$1(false);
1197
-
1198
- const openSettings = () => {
1199
- settingsOpenedSignal.value = true;
1200
- };
1201
- const closeSettings = () => {
1202
- settingsOpenedSignal.value = false;
1203
- };
1204
-
1205
- const changesTooltipOpenedSignal = d$1(false);
1206
-
1207
- const renderToolbarOverlay = () => {
1208
- const toolbarOverlay = document.querySelector("#toolbar_overlay");
1209
- toolbarOverlay.onclick = () => {
1210
- closeAllTooltips();
1211
- closeSettings();
1212
- };
1213
- E(() => {
1214
- if (!window.parent) {
1215
- // can happen while parent iframe reloads
1216
- return;
1217
- }
1218
- const opened = openedSignal.value;
1219
- const settingsOpened = settingsOpenedSignal.value;
1220
- const executionTooltipOpened = executionTooltipOpenedSignal.value;
1221
- const changesTooltipOpened = changesTooltipOpenedSignal.value;
1222
- const serverTooltipOpened = serverTooltipOpenedSignal.value;
1223
- if (!opened) {
1224
- return;
1225
- }
1226
- if (settingsOpened || executionTooltipOpened || changesTooltipOpened || serverTooltipOpened) {
1227
- enableIframeOverflowOnParentWindow();
1228
- } else {
1229
- disableIframeOverflowOnParentWindow();
1230
- }
1231
- });
1232
- };
1233
- const enableIframeOverflowOnParentWindow = () => {
1234
- const iframe = getToolbarIframe();
1235
- const transitionDuration = iframe.style.transitionDuration;
1236
- setStyles(iframe, {
1237
- "height": "100%",
1238
- // we don't want to animate height transition
1239
- // but if it was enabled, we'll restore it afterwards
1240
- "transition-duration": "0ms"
1241
- });
1242
- if (transitionDuration) {
1243
- setTimeout(() => {
1244
- setStyles(iframe, {
1245
- "transition-duration": transitionDuration
1246
- });
1247
- });
1323
+ const startJavaScriptAnimation = ({
1324
+ duration = 300,
1325
+ timingFunction = t => t,
1326
+ onProgress = () => {},
1327
+ onCancel = () => {},
1328
+ onComplete = () => {}
1329
+ }) => {
1330
+ if (isNaN(duration)) {
1331
+ // console.warn(`duration must be a number, received ${duration}`)
1332
+ return () => {};
1248
1333
  }
1249
- };
1250
- const disableIframeOverflowOnParentWindow = () => {
1251
- const iframe = getToolbarIframe();
1252
- const transitionDuration = iframe.style.transitionDuration;
1253
- setStyles(iframe, {
1254
- "height": "40px",
1255
- // we don't want to animate height transition
1256
- // but if it was enabled, we'll restore it afterwards
1257
- "transition-duration": "0ms"
1258
- });
1259
- if (transitionDuration) {
1260
- setTimeout(() => {
1261
- setStyles(iframe, {
1262
- "transition-duration": transitionDuration
1263
- });
1334
+ duration = parseInt(duration, 10);
1335
+ const startMs = performance.now();
1336
+ let currentRequestAnimationFrameId;
1337
+ let done = false;
1338
+ let rawProgress = 0;
1339
+ let progress = 0;
1340
+ const handler = () => {
1341
+ currentRequestAnimationFrameId = null;
1342
+ const nowMs = performance.now();
1343
+ rawProgress = Math.min((nowMs - startMs) / duration, 1);
1344
+ progress = timingFunction(rawProgress);
1345
+ done = rawProgress === 1;
1346
+ onProgress({
1347
+ done,
1348
+ rawProgress,
1349
+ progress
1264
1350
  });
1265
- }
1266
- };
1267
-
1268
- const setLinkHrefForParentWindow = (a, href) => {
1269
- a.href = href;
1270
- a.onclick = e => {
1271
- if (e.ctrlKey || e.metaKey) {
1272
- return;
1351
+ if (done) {
1352
+ onComplete();
1353
+ } else {
1354
+ currentRequestAnimationFrameId = window.requestAnimationFrame(handler);
1273
1355
  }
1274
- e.preventDefault();
1275
- window.parent.location.href = href;
1276
1356
  };
1277
- };
1278
-
1279
- const renderDocumentIndexLink = () => {
1280
- setLinkHrefForParentWindow(document.querySelector("#document_index_link"), "/");
1281
- };
1282
-
1283
- const enableVariant = (rootNode, variables) => {
1284
- const nodesNotMatching = Array.from(rootNode.querySelectorAll("[".concat(attributeIndicatingACondition, "]")));
1285
- nodesNotMatching.forEach(nodeNotMatching => {
1286
- const conditionAttributeValue = nodeNotMatching.getAttribute(attributeIndicatingACondition);
1287
- const matches = testCondition(conditionAttributeValue, variables);
1288
- if (matches) {
1289
- renameAttribute(nodeNotMatching, attributeIndicatingACondition, attributeIndicatingAMatch);
1290
- }
1291
- });
1292
- const nodesMatching = Array.from(rootNode.querySelectorAll("[".concat(attributeIndicatingAMatch, "]")));
1293
- nodesMatching.forEach(nodeMatching => {
1294
- const conditionAttributeValue = nodeMatching.getAttribute(attributeIndicatingAMatch);
1295
- const matches = testCondition(conditionAttributeValue, variables);
1296
- if (!matches) {
1297
- renameAttribute(nodeMatching, attributeIndicatingAMatch, attributeIndicatingACondition);
1298
- }
1299
- });
1300
- };
1301
- const testCondition = (conditionAttributeValue, variables) => {
1302
- const condition = parseCondition(conditionAttributeValue);
1303
- return Object.keys(variables).some(key => {
1304
- if (condition.key !== key) {
1305
- return false;
1306
- }
1307
- // the condition do not specify a value, any value is ok
1308
- if (condition.value === undefined) {
1309
- return true;
1357
+ handler();
1358
+ const stop = () => {
1359
+ if (currentRequestAnimationFrameId) {
1360
+ window.cancelAnimationFrame(currentRequestAnimationFrameId);
1361
+ currentRequestAnimationFrameId = null;
1310
1362
  }
1311
- if (condition.value === variables[key]) {
1312
- return true;
1363
+ if (!done) {
1364
+ done = true;
1365
+ onCancel({
1366
+ rawProgress,
1367
+ progress
1368
+ });
1313
1369
  }
1314
- return false;
1315
- });
1316
- };
1317
- const parseCondition = conditionAttributeValue => {
1318
- const colonIndex = conditionAttributeValue.indexOf(":");
1319
- if (colonIndex === -1) {
1320
- return {
1321
- key: conditionAttributeValue,
1322
- value: undefined
1323
- };
1324
- }
1325
- return {
1326
- key: conditionAttributeValue.slice(0, colonIndex),
1327
- value: conditionAttributeValue.slice(colonIndex + 1)
1328
1370
  };
1329
- };
1330
- const attributeIndicatingACondition = "data-when";
1331
- const attributeIndicatingAMatch = "data-when-active";
1332
- const renameAttribute = (node, name, newName) => {
1333
- node.setAttribute(newName, node.getAttribute(name));
1334
- node.removeAttribute(name);
1371
+ return stop;
1335
1372
  };
1336
1373
 
1337
- const executionIndicator = document.querySelector("#document_execution_indicator");
1338
- const renderDocumentExecutionIndicator = async () => {
1339
- removeForceHideElement(document.querySelector("#document_execution_indicator"));
1340
- E(() => {
1341
- const execution = executionSignal.value;
1342
- updateExecutionIndicator(execution);
1343
- });
1374
+ const initToolbarOpening = () => {
1344
1375
  E(() => {
1345
- const executionTooltipOpened = executionTooltipOpenedSignal.value;
1346
- if (executionTooltipOpened) {
1347
- executionIndicator.setAttribute("data-tooltip-visible", "");
1376
+ const opened = openedSignal.value;
1377
+ if (opened) {
1378
+ showToolbar();
1348
1379
  } else {
1349
- executionIndicator.removeAttribute("data-tooltip-visible");
1380
+ hideToolbar();
1350
1381
  }
1351
1382
  });
1352
1383
  };
1353
- const updateExecutionIndicator = ({
1354
- status,
1355
- startTime,
1356
- endTime
1357
- } = {}) => {
1358
- enableVariant(executionIndicator, {
1359
- execution: status
1360
- });
1361
- const variantNode = executionIndicator.querySelector("[data-when-active]");
1362
- variantNode.querySelector("button").onclick = () => {
1363
- const executionTooltipOpened = executionTooltipOpenedSignal.value;
1364
- if (executionTooltipOpened) {
1365
- closeExecutionTooltip();
1366
- } else {
1367
- openExecutionTooltip();
1368
- }
1369
- };
1370
- variantNode.querySelector(".tooltip").textContent = computeText({
1371
- status,
1372
- startTime,
1373
- endTime
1374
- });
1384
+ let restoreToolbarIframeParentStyles = () => {};
1385
+ let restoreToolbarIframeStyles = () => {};
1386
+ const hideToolbar = () => {
1387
+ closeAllTooltips();
1388
+ restoreToolbarIframeParentStyles();
1389
+ restoreToolbarIframeStyles();
1390
+ document.documentElement.removeAttribute("data-toolbar-visible");
1375
1391
  };
1376
1392
 
1377
- // relative time: https://github.com/tc39/proposal-intl-relative-time/issues/118
1378
- const computeText = ({
1379
- status,
1380
- startTime,
1381
- endTime
1382
- }) => {
1383
- if (status === "completed") {
1384
- return "Execution completed in ".concat(endTime - startTime, "ms");
1385
- }
1386
- if (status === "failed") {
1387
- return "Execution failed in ".concat(endTime - startTime, "ms");
1388
- }
1389
- if (status === "running") {
1390
- return "Executing...";
1393
+ // (by the way it might be cool to have the toolbar auto show when)
1394
+ // it has something to say (being disconnected from server)
1395
+ const showToolbar = () => {
1396
+ const animationsEnabled = animationsEnabledSignal.peek();
1397
+ document.documentElement.setAttribute("data-toolbar-visible", "");
1398
+ const toolbarIframe = getToolbarIframe();
1399
+ const toolbarIframeParent = toolbarIframe.parentNode;
1400
+ const parentWindow = window.parent;
1401
+ const parentDocumentElement = parentWindow.document.compatMode === "CSS1Compat" ? parentWindow.document.documentElement : parentWindow.document.body;
1402
+ const scrollYMax = parentDocumentElement.scrollHeight - parentWindow.innerHeight;
1403
+ const scrollY = parentDocumentElement.scrollTop;
1404
+ const scrollYRemaining = scrollYMax - scrollY;
1405
+ setStyles(toolbarIframeParent, {
1406
+ "transition-property": "padding-bottom",
1407
+ "transition-duration": animationsEnabled ? "300ms" : "0s"
1408
+ });
1409
+ // maybe we should use js animation here because we would not conflict with css
1410
+ restoreToolbarIframeParentStyles = setStyles(toolbarIframeParent, {
1411
+ "scroll-padding-bottom": "40px",
1412
+ // same here we should add 40px
1413
+ "padding-bottom": "40px" // if there is already one we should add 40px
1414
+ });
1415
+ restoreToolbarIframeStyles = setStyles(toolbarIframe, {
1416
+ height: "40px",
1417
+ visibility: "visible"
1418
+ });
1419
+ if (scrollYRemaining < 40 && scrollYMax > 0) {
1420
+ const scrollEnd = scrollY + 40;
1421
+ startJavaScriptAnimation({
1422
+ duration: 300,
1423
+ onProgress: ({
1424
+ progress
1425
+ }) => {
1426
+ const value = scrollY + (scrollEnd - scrollY) * progress;
1427
+ parentDocumentElement.scrollTop = value;
1428
+ }
1429
+ });
1391
1430
  }
1392
- return "";
1393
1431
  };
1394
1432
 
1395
- const autoreloadEnabledSignal = d$1(false);
1396
- const reloaderStatusSignal = d$1("idle");
1397
- const changesSignal = d$1(0);
1398
- if (parentWindowReloader) {
1399
- autoreloadEnabledSignal.value = parentWindowReloader.autoreload.enabled;
1400
- parentWindowReloader.autoreload.onchange = () => {
1401
- autoreloadEnabledSignal.value = parentWindowReloader.autoreload.enabled;
1402
- };
1403
- reloaderStatusSignal.value = parentWindowReloader.status.value;
1404
- const onchange = parentWindowReloader.status.onchange;
1405
- parentWindowReloader.status.onchange = () => {
1406
- onchange();
1407
- reloaderStatusSignal.value = parentWindowReloader.status.value;
1408
- };
1409
- changesSignal.value = parentWindowReloader.changes.value;
1410
- parentWindowReloader.changes.onchange = () => {
1411
- changesSignal.value = [...parentWindowReloader.changes.value];
1412
- };
1413
- }
1414
-
1415
- const openChangesToolip = () => {
1416
- changesTooltipOpenedSignal.value = true;
1417
- };
1418
- const closeChangesToolip = () => {
1419
- changesTooltipOpenedSignal.value = false;
1420
- };
1433
+ const settingsOpenedSignal = d$1(false);
1421
1434
 
1422
- const changesIndicator = document.querySelector("#changes_indicator");
1423
- const renderChangesIndicator = () => {
1424
- E(() => {
1425
- const autoreloadEnabled = autoreloadEnabledSignal.value;
1426
- const changes = changesSignal.value;
1427
- const changeCount = changes.length;
1428
- enableVariant(changesIndicator, {
1429
- changes: !autoreloadEnabled && changeCount ? "yes" : "no"
1430
- });
1431
- if (changeCount) {
1432
- changesIndicator.querySelector(".tooltip_text").innerHTML = computeTooltipText({
1433
- changes
1434
- });
1435
- changesIndicator.querySelector(".tooltip_text a").onclick = () => {
1436
- // eslint-disable-next-line no-alert
1437
- window.alert(JSON.stringify(changes, null, " "));
1438
- console.log(changes);
1439
- };
1440
- changesIndicator.querySelector(".changes_text").innerHTML = changeCount;
1441
- }
1442
- });
1443
- changesIndicator.querySelector(".tooltip_action").onclick = () => {
1444
- parentWindowReloader.reload();
1435
+ const openSettings = () => {
1436
+ settingsOpenedSignal.value = true;
1437
+ };
1438
+ const closeSettings = () => {
1439
+ settingsOpenedSignal.value = false;
1440
+ };
1441
+
1442
+ const renderToolbarOverlay = () => {
1443
+ const toolbarOverlay = document.querySelector("#toolbar_overlay");
1444
+ toolbarOverlay.onclick = () => {
1445
+ closeAllTooltips();
1446
+ closeSettings();
1445
1447
  };
1446
1448
  E(() => {
1447
- const changesTooltipOpened = changesTooltipOpenedSignal.value;
1448
- if (changesTooltipOpened) {
1449
- changesIndicator.setAttribute("data-tooltip-visible", "");
1450
- } else {
1451
- changesIndicator.removeAttribute("data-tooltip-visible");
1449
+ if (!window.parent) {
1450
+ // can happen while parent iframe reloads
1451
+ return;
1452
1452
  }
1453
- });
1454
- const button = changesIndicator.querySelector("button");
1455
- button.onclick = () => {
1453
+ const opened = openedSignal.value;
1454
+ const settingsOpened = settingsOpenedSignal.value;
1455
+ const executionTooltipOpened = executionTooltipOpenedSignal.value;
1456
1456
  const changesTooltipOpened = changesTooltipOpenedSignal.value;
1457
- if (changesTooltipOpened) {
1458
- closeChangesToolip();
1457
+ const serverTooltipOpened = serverTooltipOpenedSignal.value;
1458
+ if (!opened) {
1459
+ return;
1460
+ }
1461
+ if (settingsOpened || executionTooltipOpened || changesTooltipOpened || serverTooltipOpened) {
1462
+ enableIframeOverflowOnParentWindow();
1459
1463
  } else {
1460
- openChangesToolip();
1464
+ disableIframeOverflowOnParentWindow();
1461
1465
  }
1462
- };
1466
+ });
1463
1467
  };
1464
- const computeTooltipText = ({
1465
- changes
1466
- }) => {
1467
- const changesCount = changes.length;
1468
- if (changesCount === 1) {
1469
- return "There is <a href=\"javascript:void(0)\">1</a> change to apply";
1468
+ const enableIframeOverflowOnParentWindow = () => {
1469
+ const iframe = getToolbarIframe();
1470
+ const transitionDuration = iframe.style.transitionDuration;
1471
+ setStyles(iframe, {
1472
+ "height": "100%",
1473
+ // we don't want to animate height transition
1474
+ // but if it was enabled, we'll restore it afterwards
1475
+ "transition-duration": "0ms"
1476
+ });
1477
+ if (transitionDuration) {
1478
+ setTimeout(() => {
1479
+ setStyles(iframe, {
1480
+ "transition-duration": transitionDuration
1481
+ });
1482
+ });
1483
+ }
1484
+ };
1485
+ const disableIframeOverflowOnParentWindow = () => {
1486
+ const iframe = getToolbarIframe();
1487
+ const transitionDuration = iframe.style.transitionDuration;
1488
+ setStyles(iframe, {
1489
+ "height": "40px",
1490
+ // we don't want to animate height transition
1491
+ // but if it was enabled, we'll restore it afterwards
1492
+ "transition-duration": "0ms"
1493
+ });
1494
+ if (transitionDuration) {
1495
+ setTimeout(() => {
1496
+ setStyles(iframe, {
1497
+ "transition-duration": transitionDuration
1498
+ });
1499
+ });
1470
1500
  }
1471
- return "There is <a href=\"javascript:void(0)\">".concat(changesCount, "<a> changes to apply");
1472
1501
  };
1473
1502
 
1474
- const parentServerEvents = window.parent.__server_events__;
1475
- const serverEvents = window.__server_events__;
1476
- const serverIndicator = document.querySelector("#server_indicator");
1477
- const renderServerIndicator = () => {
1478
- removeForceHideElement(document.querySelector("#server_indicator"));
1503
+ const enableAnimations = () => {
1504
+ animationsEnabledSignal.value = true;
1505
+ };
1506
+ const disableAnimations = () => {
1507
+ animationsEnabledSignal.value = false;
1508
+ };
1509
+
1510
+ const renderToolbarAnimationSetting = () => {
1511
+ const animCheckbox = document.querySelector("#toggle_anims");
1479
1512
  E(() => {
1480
- const serverConnection = serverConnectionSignal.value;
1481
- updateServerIndicator(serverConnection);
1513
+ const animationsEnabled = animationsEnabledSignal.value;
1514
+ animCheckbox.checked = animationsEnabled;
1482
1515
  });
1483
- E(() => {
1484
- const serverTooltipOpened = serverTooltipOpenedSignal.value;
1485
- if (serverTooltipOpened) {
1486
- serverIndicator.setAttribute("data-tooltip-visible", "");
1516
+ animCheckbox.onchange = () => {
1517
+ if (animCheckbox.checked) {
1518
+ enableAnimations();
1487
1519
  } else {
1488
- serverIndicator.removeAttribute("data-tooltip-visible");
1520
+ disableAnimations();
1489
1521
  }
1522
+ };
1523
+ // enable toolbar transition only after first render
1524
+ setTimeout(() => {
1525
+ document.querySelector("#toolbar").setAttribute("data-animate", "");
1490
1526
  });
1491
1527
  };
1492
- const updateServerIndicator = connectionState => {
1493
- enableVariant(serverIndicator, {
1494
- connectionState
1495
- });
1496
- const variantNode = document.querySelector("#server_indicator > [data-when-active]");
1497
- variantNode.querySelector("button").onclick = () => {
1498
- const serverTooltipOpened = serverTooltipOpenedSignal.value;
1499
- if (serverTooltipOpened) {
1500
- closeServerTooltip();
1501
- } else {
1502
- openServerTooltip();
1528
+
1529
+ const sendEventToParent = (name, data) => {
1530
+ window.parent.postMessage({
1531
+ __jsenv__: {
1532
+ event: name,
1533
+ data
1534
+ }
1535
+ }, "*");
1536
+ };
1537
+ const addExternalCommandCallback = (command, callback) => {
1538
+ const messageEventCallback = messageEvent => {
1539
+ const {
1540
+ data
1541
+ } = messageEvent;
1542
+ if (typeof data !== "object") {
1543
+ return;
1544
+ }
1545
+ const {
1546
+ __jsenv__
1547
+ } = data;
1548
+ if (!__jsenv__) {
1549
+ return;
1550
+ }
1551
+ if (__jsenv__.command !== command) {
1552
+ return;
1503
1553
  }
1554
+ callback(...__jsenv__.args);
1504
1555
  };
1505
- if (connectionState === "connecting") {
1506
- variantNode.querySelector("a").onclick = () => {
1507
- if (parentServerEvents) {
1508
- parentServerEvents.disconnect();
1509
- }
1510
- serverEvents.disconnect();
1511
- };
1512
- } else if (connectionState === "closed") {
1513
- variantNode.querySelector("a").onclick = () => {
1514
- if (parentServerEvents) {
1515
- parentServerEvents.connect();
1516
- }
1517
- serverEvents.connect();
1518
- };
1519
- }
1556
+ window.addEventListener("message", messageEventCallback);
1557
+ return () => {
1558
+ window.removeEventListener("message", messageEventCallback);
1559
+ };
1560
+ };
1561
+ const enableAutoreload = () => {
1562
+ parentWindowReloader.autoreload.enable();
1563
+ };
1564
+ const disableAutoreload = () => {
1565
+ parentWindowReloader.autoreload.disable();
1520
1566
  };
1521
1567
 
1522
1568
  const renderToolbarAutoreloadSetting = () => {
@@ -1584,32 +1630,6 @@ const disableAutoreloadSetting = () => {
1584
1630
  // updateEventSourceIndicator()
1585
1631
  // }
1586
1632
 
1587
- const enableAnimations = () => {
1588
- animationsEnabledSignal.value = true;
1589
- };
1590
- const disableAnimations = () => {
1591
- animationsEnabledSignal.value = false;
1592
- };
1593
-
1594
- const renderToolbarAnimationSetting = () => {
1595
- const animCheckbox = document.querySelector("#toggle_anims");
1596
- E(() => {
1597
- const animationsEnabled = animationsEnabledSignal.value;
1598
- animCheckbox.checked = animationsEnabled;
1599
- });
1600
- animCheckbox.onchange = () => {
1601
- if (animCheckbox.checked) {
1602
- enableAnimations();
1603
- } else {
1604
- disableAnimations();
1605
- }
1606
- };
1607
- // enable toolbar transition only after first render
1608
- setTimeout(() => {
1609
- document.querySelector("#toolbar").setAttribute("data-animate", "");
1610
- });
1611
- };
1612
-
1613
1633
  const notifCheckbox = document.querySelector("#toggle_notifs");
1614
1634
  const renderToolbarNotificationSetting = () => {
1615
1635
  E(() => {
@@ -1678,27 +1698,6 @@ const applyNotificationNOTGrantedEffects = () => {
1678
1698
  };
1679
1699
  };
1680
1700
 
1681
- const themeSignal = d$1(typeof stateFromLocalStorage.theme === "string" ? stateFromLocalStorage.theme : typeof paramsFromParentWindow.theme === "string" ? paramsFromParentWindow.theme : "dark");
1682
-
1683
- const switchToLightTheme = () => {
1684
- themeSignal.value = "light";
1685
- };
1686
- const switchToDefaultTheme = () => {
1687
- themeSignal.value = "dark";
1688
- };
1689
-
1690
- const renderToolbarThemeSetting = () => {
1691
- const checkbox = document.querySelector("#checkbox_dark_theme");
1692
- checkbox.checked = themeSignal.value === "dark";
1693
- checkbox.onchange = () => {
1694
- if (checkbox.checked) {
1695
- switchToDefaultTheme();
1696
- } else {
1697
- switchToLightTheme();
1698
- }
1699
- };
1700
- };
1701
-
1702
1701
  const ribbonDisplayedSignal = d$1(typeof stateFromLocalStorage.ribbonDisplayed === "boolean" ? stateFromLocalStorage.ribbonDisplayed : true);
1703
1702
 
1704
1703
  const ribbonBox = document.querySelector("#ribbon_box");
@@ -1726,6 +1725,27 @@ const renderToolbarRibbonSetting = () => {
1726
1725
  }
1727
1726
  };
1728
1727
 
1728
+ const themeSignal = d$1(typeof stateFromLocalStorage.theme === "string" ? stateFromLocalStorage.theme : typeof paramsFromParentWindow.theme === "string" ? paramsFromParentWindow.theme : "dark");
1729
+
1730
+ const switchToLightTheme = () => {
1731
+ themeSignal.value = "light";
1732
+ };
1733
+ const switchToDefaultTheme = () => {
1734
+ themeSignal.value = "dark";
1735
+ };
1736
+
1737
+ const renderToolbarThemeSetting = () => {
1738
+ const checkbox = document.querySelector("#checkbox_dark_theme");
1739
+ checkbox.checked = themeSignal.value === "dark";
1740
+ checkbox.onchange = () => {
1741
+ if (checkbox.checked) {
1742
+ switchToDefaultTheme();
1743
+ } else {
1744
+ switchToLightTheme();
1745
+ }
1746
+ };
1747
+ };
1748
+
1729
1749
  const renderToolbarSettings = () => {
1730
1750
  document.querySelector("#settings_open_button").onclick = toggleSettings;
1731
1751
  document.querySelector("#settings_close_button").onclick = toggleSettings;
@@ -1758,26 +1778,6 @@ const disableWarningStyle = () => {
1758
1778
  });
1759
1779
  };
1760
1780
 
1761
- const openToolbar = () => {
1762
- openedSignal.value = true;
1763
- };
1764
- const closeToolbar = () => {
1765
- openedSignal.value = false;
1766
- };
1767
-
1768
- const renderToolbarCloseButton = () => {
1769
- // if user click enter or space quickly while closing toolbar
1770
- // it will cancel the closing
1771
- // that's why I used toggleToolbar and not hideToolbar
1772
- document.querySelector("#toolbar_close_button").onclick = () => {
1773
- if (openedSignal.value) {
1774
- closeToolbar();
1775
- } else {
1776
- openToolbar();
1777
- }
1778
- };
1779
- };
1780
-
1781
1781
  const initToolbarUI = () => {
1782
1782
  initToolbarOpening();
1783
1783
  initToolbarMenuOverflow();