diff --git a/Specialized Areas/Browser Bookmarklets/Open g_form modal/README.md b/Specialized Areas/Browser Bookmarklets/Open g_form modal/README.md
new file mode 100644
index 0000000000..fa5d812e23
--- /dev/null
+++ b/Specialized Areas/Browser Bookmarklets/Open g_form modal/README.md
@@ -0,0 +1,18 @@
+# g_form Bookmarklet Modal
+
+This script provides a bookmarklet that injects a draggable modal into ServiceNow forms that allows the user to interact with fields on forms. Tested on next exp classic forms with or without app shell, workspace(needs snutils installed) and portal.
+---
+
+## 📸 Screenshots
+
+### Modal Overview
+
+
+---
+
+## 🔧 How to Use
+
+1. **Copy the minified script** gFormModal.min.js
+2. **Create a bookmark** in your browser.
+3. Paste the script into the bookmark's URL field.
+4. Navigate to a ServiceNow form and click the bookmarklet.
diff --git a/Specialized Areas/Browser Bookmarklets/Open g_form modal/gFormModal.js b/Specialized Areas/Browser Bookmarklets/Open g_form modal/gFormModal.js
new file mode 100644
index 0000000000..3dcaf19ccf
--- /dev/null
+++ b/Specialized Areas/Browser Bookmarklets/Open g_form modal/gFormModal.js
@@ -0,0 +1,193 @@
+(function () {
+ const makeDraggable = (element, cleanupHandlers) => {
+ let isDragging = false;
+ let offsetX = 0;
+ let offsetY = 0;
+
+ const header = document.createElement('div');
+ header.style.cursor = 'move';
+ header.style.height = '20px';
+ header.style.marginBottom = '10px';
+ header.style.background = "rgba(100, 100, 100, 0.3)";
+ header.style.borderRadius = "8px";
+ element.prepend(header);
+
+ const mouseMoveHandler = (e) => {
+ if (isDragging) {
+ element.style.left = `${e.clientX - offsetX}px`;
+ element.style.top = `${e.clientY - offsetY}px`;
+ element.style.position = 'absolute';
+ }
+ };
+
+ const mouseUpHandler = () => {
+ isDragging = false;
+ document.body.style.userSelect = '';
+ };
+
+ header.addEventListener('mousedown', (e) => {
+ isDragging = true;
+ offsetX = e.clientX - element.offsetLeft;
+ offsetY = e.clientY - element.offsetTop;
+ document.body.style.userSelect = 'none';
+ });
+
+ document.addEventListener('mousemove', mouseMoveHandler);
+ document.addEventListener('mouseup', mouseUpHandler);
+
+ cleanupHandlers.push(() => {
+ document.removeEventListener('mousemove', mouseMoveHandler);
+ document.removeEventListener('mouseup', mouseUpHandler);
+ });
+ };
+
+ const createOverlay = (g_form, fieldArray) => {
+ const overlay = document.createElement('div');
+ overlay.id = "gform-modal-overlay";
+ overlay.style.position = 'fixed';
+ overlay.style.top = 0;
+ overlay.style.left = 0;
+ overlay.style.width = '100vw';
+ overlay.style.height = '100vh';
+ overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.6)';
+ overlay.style.zIndex = 9999;
+ overlay.style.display = 'flex';
+ overlay.style.justifyContent = 'center';
+ overlay.style.alignItems = 'center';
+
+
+ const container = document.createElement('div');
+ container.id = "modal-container";
+ container.style.background = '#fff';
+ container.style.padding = '20px';
+ container.style.borderRadius = '8px';
+ container.style.boxShadow = '0 0 10px rgba(0,0,0,0.3)';
+ container.style.maxWidth = '1000px';
+ container.style.textAlign = 'center';
+ container.style.maxHeight = '70vh';
+
+ const modal = document.createElement('div');
+ modal.style.overflowY = 'auto';
+ modal.style.overflowX = 'hidden';
+ modal.style.maxHeight = '60vh';
+ container.appendChild(modal)
+
+ let listItems = '';
+ fieldArray.forEach(element => {
+ const fieldName = element.fieldName;
+ const isReadOnly = g_form.isReadOnly(fieldName);
+ const isMandatory = g_form.isMandatory(fieldName);
+ const isVisible = g_form.isVisible(fieldName);
+
+ listItems += `
+
+ | ${fieldName} |
+ |
+ |
+ |
+ |
+ |
+
+ `;
+ });
+
+ modal.innerHTML = `
+ g_form modal
+
+
+
+ | Field |
+ ReadOnly |
+ Mandatory |
+ Display |
+ Value |
+ |
+
+
+ ${listItems}
+
+ `;
+
+ const cleanupHandlers = [];
+
+ overlay.addEventListener('click', (event) => {
+ if (!container.contains(event.target)) {
+ cleanupHandlers.forEach(fn => fn());
+ document.body.removeChild(overlay);
+ }
+ });
+
+ makeDraggable(container, cleanupHandlers);
+ overlay.appendChild(container);
+ document.body.appendChild(overlay);
+
+ fieldArray.forEach(element => {
+ const fieldName = element.fieldName;
+ const disabledCheckbox = modal.querySelector(`#disabled-${fieldName}`);
+ const mandatoryCheckbox = modal.querySelector(`#mandatory-${fieldName}`);
+ const visibleCheckbox = modal.querySelector(`#visible-${fieldName}`);
+ const valueInput = modal.querySelector(`#value-${fieldName}`);
+ const changeButton = modal.querySelector(`#change-value-${fieldName}`);
+
+ if (disabledCheckbox) {
+ disabledCheckbox.addEventListener('change', () => {
+ g_form?.setDisabled(fieldName, disabledCheckbox.checked);
+ });
+ }
+
+ if (mandatoryCheckbox) {
+ mandatoryCheckbox.addEventListener('change', () => {
+ const setToMandatory = mandatoryCheckbox.checked;
+ const isEmpty = !g_form?.getValue(fieldName);
+ const isDisplayed = visibleCheckbox.checked;
+ g_form?.setMandatory(fieldName, setToMandatory);
+
+ if (setToMandatory) {
+ if (!isEmpty && isDisplayed) {
+ visibleCheckbox.checked = true;
+ } else if (isEmpty && !isDisplayed) {
+ visibleCheckbox.checked = true;
+ disabledCheckbox.checked = false;
+ } else if (isEmpty && isDisplayed) {
+ disabledCheckbox.checked = false;
+ }
+ }
+ if (disabledCheckbox) disabledCheckbox.disabled = setToMandatory;
+ if (visibleCheckbox) visibleCheckbox.disabled = setToMandatory;
+ });
+ }
+
+ if (visibleCheckbox) {
+ visibleCheckbox.addEventListener('change', () => {
+ g_form?.setDisplay(fieldName, visibleCheckbox.checked);
+ });
+ }
+
+ if (changeButton && valueInput) {
+ changeButton.addEventListener('click', () => {
+ const newValue = valueInput.value;
+ g_form.setValue(fieldName, newValue);
+ });
+ }
+ });
+ };
+
+ if (document.querySelector("#gform-modal-overlay")) return;
+
+ const getFieldNames = (g_form) => {
+ return g_form.getFieldNames().map(name => ({ fieldName: name }));
+ }
+
+ const g_form =
+ window.gsft_main?.g_form ||
+ this.g_form ||
+ (typeof querySelectorShadowDom !== "undefined" &&
+ querySelectorShadowDom.querySelectorAllDeep('sn-form-data-connected')[0]?.nowRecordFormBlob?.gForm) ||
+ (angular.element("sp-variable-layout").scope().getGlideForm?.()) ||
+ null;
+ if (!g_form) {
+ return;
+ }
+ const fieldArray = g_form.elements ? g_form.elements : getFieldNames(g_form);
+ createOverlay(g_form, fieldArray);
+})();
diff --git a/Specialized Areas/Browser Bookmarklets/Open g_form modal/gFormModal.min.js b/Specialized Areas/Browser Bookmarklets/Open g_form modal/gFormModal.min.js
new file mode 100644
index 0000000000..68b7ddf34b
--- /dev/null
+++ b/Specialized Areas/Browser Bookmarklets/Open g_form modal/gFormModal.min.js
@@ -0,0 +1 @@
+javascript:!function(){if(document.querySelector("#gform-modal-overlay"))return;const e=window.gsft_main?.g_form||this.g_form||"undefined"!=typeof querySelectorShadowDom&&querySelectorShadowDom.querySelectorAllDeep("sn-form-data-connected")[0]?.nowRecordFormBlob?.gForm||angular.element("sp-variable-layout").scope().getGlideForm?.()||null;if(!e)return;const t=e.elements?e.elements:(e=>e.getFieldNames().map((e=>({fieldName:e}))))(e);((e,t)=>{const n=document.createElement("div");n.id="gform-modal-overlay",n.style.position="fixed",n.style.top=0,n.style.left=0,n.style.width="100vw",n.style.height="100vh",n.style.backgroundColor="rgba(0, 0, 0, 0.6)",n.style.zIndex=9999,n.style.display="flex",n.style.justifyContent="center",n.style.alignItems="center";const d=document.createElement("div");d.id="modal-container",d.style.background="#fff",d.style.padding="20px",d.style.borderRadius="8px",d.style.boxShadow="0 0 10px rgba(0,0,0,0.3)",d.style.maxWidth="1000px",d.style.textAlign="center",d.style.maxHeight="70vh";const o=document.createElement("div");o.style.overflowY="auto",o.style.overflowX="hidden",o.style.maxHeight="60vh",d.appendChild(o);let l="";t.forEach((t=>{const n=t.fieldName,d=e.isReadOnly(n),o=e.isMandatory(n),a=e.isVisible(n);l+=`\n \n | ${n} | \n | \n | \n | \n | \n | \n
\n `})),o.innerHTML=`\n g_form modal
\n \n \n \n | Field | \n ReadOnly | \n Mandatory | \n Display | \n Value | \n | \n
\n \n ${l}\n
\n `;const a=[];n.addEventListener("click",(e=>{d.contains(e.target)||(a.forEach((e=>e())),document.body.removeChild(n))})),((e,t)=>{let n=!1,d=0,o=0;const l=document.createElement("div");l.style.cursor="move",l.style.height="20px",l.style.marginBottom="10px",l.style.background="rgba(100, 100, 100, 0.3)",l.style.borderRadius="8px",e.prepend(l);const a=t=>{n&&(e.style.left=t.clientX-d+"px",e.style.top=t.clientY-o+"px",e.style.position="absolute")},s=()=>{n=!1,document.body.style.userSelect=""};l.addEventListener("mousedown",(t=>{n=!0,d=t.clientX-e.offsetLeft,o=t.clientY-e.offsetTop,document.body.style.userSelect="none"})),document.addEventListener("mousemove",a),document.addEventListener("mouseup",s),t.push((()=>{document.removeEventListener("mousemove",a),document.removeEventListener("mouseup",s)}))})(d,a),n.appendChild(d),document.body.appendChild(n),t.forEach((t=>{const n=t.fieldName,d=o.querySelector(`#disabled-${n}`),l=o.querySelector(`#mandatory-${n}`),a=o.querySelector(`#visible-${n}`),s=o.querySelector(`#value-${n}`),c=o.querySelector(`#change-value-${n}`);d&&d.addEventListener("change",(()=>{e?.setDisabled(n,d.checked)})),l&&l.addEventListener("change",(()=>{const t=l.checked,o=!e?.getValue(n),s=a.checked;e?.setMandatory(n,t),t&&(!o&&s?a.checked=!0:o&&!s?(a.checked=!0,d.checked=!1):o&&s&&(d.checked=!1)),d&&(d.disabled=t),a&&(a.disabled=t)})),a&&a.addEventListener("change",(()=>{e?.setDisplay(n,a.checked)})),c&&s&&c.addEventListener("click",(()=>{const t=s.value;e.setValue(n,t)}))}))})(e,t)}();
diff --git a/Specialized Areas/Browser Bookmarklets/Open g_form modal/image.png b/Specialized Areas/Browser Bookmarklets/Open g_form modal/image.png
new file mode 100644
index 0000000000..8d656fb079
Binary files /dev/null and b/Specialized Areas/Browser Bookmarklets/Open g_form modal/image.png differ