{
  "version": "1.0.0",
  "exported_at": "2026-06-01T17:50:00.000Z",
  "project": {
    "name": "Tripcom Scraper Listing URL",
    "description": "Extracts Trip.com hotel listing data and hotel detail URLs from a Trip.com hotel search/listing URL. Includes a sign-in guard because Trip.com may redirect anonymous sessions to sign-in. For accessible listing sessions, it exports unprocessed hotel detail links, marks them as exported, scrolls/wheels the page and scrollable containers while waiting for virtualized/lazy hotel links, optionally clicks listing-style Load more / More hotels / More properties controls, then checks visible result pagination. The fallback intentionally excludes generic 'Show More' buttons because Trip.com uses those inside hotel cards/modals.",
    "color": "bg-[#4589ff]",
    "template_id": "ai-generated"
  },
  "blocks": [
    {
      "block_id": "set-window-size-1",
      "block_type": "process",
      "title": "Set Window Size",
      "description": "Set browser window dimensions",
      "position_x": 120,
      "position_y": 220,
      "config": {
        "width": 1920,
        "height": 1080,
        "color": "bg-[#4589ff]"
      }
    },
    {
      "block_id": "navigate-1",
      "block_type": "process",
      "title": "Navigate",
      "description": "Go to a URL",
      "position_x": 456,
      "position_y": 220,
      "config": {
        "url": "https://www.trip.com/hotels/list?city=633&cityName=New%20York&provinceId=0&countryId=66&districtId=0&checkin=2024/04/10&checkout=2024/04/11&barCurr=HKD&searchType=CT&searchWord=New%20York&crn=1&adult=2&children=0&searchBoxArg=t&travelPurpose=0&ctm_ref=ix_sb_dl&domestic=true",
        "color": "bg-[#4589ff]"
      }
    },
    {
      "block_id": "wait-for-page-load-1",
      "block_type": "process",
      "title": "Wait for Page Load",
      "description": "Wait for page to finish loading",
      "position_x": 792,
      "position_y": 220,
      "config": {
        "timeout": 45,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 1128,
      "position_y": 220,
      "config": {
        "duration": 4,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "element-exists-1",
      "block_type": "process",
      "title": "Element Exists",
      "description": "Check if element exists",
      "position_x": 1464,
      "position_y": 220,
      "config": {
        "selector": "#ibu_login_online input[placeholder*=\"email\"], form input[placeholder*=\"email\"], canvas.pb7GO",
        "color": "bg-[#ff832b]"
      }
    },
    {
      "block_id": "end-2",
      "block_type": "output",
      "title": "End",
      "description": "Terminate execution flow",
      "position_x": 1800,
      "position_y": 520,
      "config": {
        "color": "bg-[#8d8d8d]"
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 1464,
      "position_y": 520,
      "config": {
        "selector": "a[href*=\"/hotels/detail/\"], a[href*=\"/hotels/v2/detail/\"]",
        "timeout": 60,
        "visible": true,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 1968,
      "position_y": 800,
      "config": {
        "rowSelector": "a[href*=\"/hotels/detail/\"]:not([data-uscraper-exported]), a[href*=\"/hotels/v2/detail/\"]:not([data-uscraper-exported])",
        "fileName": "trip-com-scraper-listing-url.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "color": "bg-[#42be65]",
        "columns": [
          {
            "name": "Web_Page_URL",
            "selector": "window.location.href",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Hotel_Name",
            "selector": "(() => { const getId = a => { try { const u = new URL(a.href || a.getAttribute('href'), location.href); return u.searchParams.get('hotelId') || u.href; } catch(e) { return a.getAttribute('href') || ''; } }; let card = ROW; for (let i = 0; i < 10 && card.parentElement; i++) { const p = card.parentElement; const ids = new Set(Array.from(p.querySelectorAll('a[href*=\"/hotels/detail/\"],a[href*=\"/hotels/v2/detail/\"]')).map(getId).filter(Boolean)); const txt = p.innerText || ''; if (ids.size > 1 || txt.length > 3500 || /\\d[\\d,]*\\s+properties found/i.test(txt)) break; card = p; } const direct = (ROW.getAttribute('title') || ROW.getAttribute('aria-label') || ROW.textContent || '').trim(); if (direct && !/view|select|room|detail|book/i.test(direct)) return direct; const lines = (card.innerText || '').split('\\n').map(s => s.trim()).filter(Boolean); return lines.find(l => l.length > 3 && !/review|show on map|properties found|sort by|per room|tax|fee|US\\$|HK\\$|\\d+\\/5|select|view|book/i.test(l)) || direct; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Rating",
            "selector": "(() => { const getId = a => { try { const u = new URL(a.href || a.getAttribute('href'), location.href); return u.searchParams.get('hotelId') || u.href; } catch(e) { return a.getAttribute('href') || ''; } }; let card = ROW; for (let i = 0; i < 10 && card.parentElement; i++) { const p = card.parentElement; const ids = new Set(Array.from(p.querySelectorAll('a[href*=\"/hotels/detail/\"],a[href*=\"/hotels/v2/detail/\"]')).map(getId).filter(Boolean)); const txt = p.innerText || ''; if (ids.size > 1 || txt.length > 3500 || /\\d[\\d,]*\\s+properties found/i.test(txt)) break; card = p; } const txt = card.innerText || ''; let m = txt.match(/\\b[0-5](?:\\.\\d)?\\s*\\/\\s*5\\b/); if (m) return m[0].replace(/\\s+/g, ''); m = txt.match(/\\b[3-5]\\.\\d\\b/); return m ? m[0] + '/5' : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Star_Rating",
            "selector": "(() => { const getId = a => { try { const u = new URL(a.href || a.getAttribute('href'), location.href); return u.searchParams.get('hotelId') || u.href; } catch(e) { return a.getAttribute('href') || ''; } }; let card = ROW; for (let i = 0; i < 10 && card.parentElement; i++) { const p = card.parentElement; const ids = new Set(Array.from(p.querySelectorAll('a[href*=\"/hotels/detail/\"],a[href*=\"/hotels/v2/detail/\"]')).map(getId).filter(Boolean)); const txt = p.innerText || ''; if (ids.size > 1 || txt.length > 3500 || /\\d[\\d,]*\\s+properties found/i.test(txt)) break; card = p; } const txt = (card.innerText || '') + ' ' + Array.from(card.querySelectorAll('[aria-label], [title]')).map(e => e.getAttribute('aria-label') || e.getAttribute('title') || '').join(' '); const m = txt.match(/\\b([1-5])\\s*(?:star|stars|diamond|diamonds)\\b/i); if (m) return m[1]; const starEls = Array.from(card.querySelectorAll('[class*=\"star\" i], [aria-label*=\"star\" i], [title*=\"star\" i]')).filter(e => e.offsetParent !== null); return starEls.length > 0 && starEls.length <= 5 ? String(starEls.length) : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Number_of_Reviews",
            "selector": "(() => { const getId = a => { try { const u = new URL(a.href || a.getAttribute('href'), location.href); return u.searchParams.get('hotelId') || u.href; } catch(e) { return a.getAttribute('href') || ''; } }; let card = ROW; for (let i = 0; i < 10 && card.parentElement; i++) { const p = card.parentElement; const ids = new Set(Array.from(p.querySelectorAll('a[href*=\"/hotels/detail/\"],a[href*=\"/hotels/v2/detail/\"]')).map(getId).filter(Boolean)); const txt = p.innerText || ''; if (ids.size > 1 || txt.length > 3500 || /\\d[\\d,]*\\s+properties found/i.test(txt)) break; card = p; } const txt = card.innerText || ''; const matches = txt.match(/\\b\\d[\\d,]*\\s+reviews?\\b/ig); if (!matches || matches.length === 0) return ''; const candidate = matches[0]; const all = document.body.innerText || ''; return (all.split(candidate).length - 1) > 4 ? '' : candidate; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Location",
            "selector": "(() => { const getId = a => { try { const u = new URL(a.href || a.getAttribute('href'), location.href); return u.searchParams.get('hotelId') || u.href; } catch(e) { return a.getAttribute('href') || ''; } }; let card = ROW; for (let i = 0; i < 10 && card.parentElement; i++) { const p = card.parentElement; const ids = new Set(Array.from(p.querySelectorAll('a[href*=\"/hotels/detail/\"],a[href*=\"/hotels/v2/detail/\"]')).map(getId).filter(Boolean)); const txt = p.innerText || ''; if (ids.size > 1 || txt.length > 3500 || /\\d[\\d,]*\\s+properties found/i.test(txt)) break; card = p; } const lines = (card.innerText || '').split('\\n').map(s => s.trim()).filter(Boolean); return lines.find(l => /show on map|near\\s+/i.test(l)) || ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Price",
            "selector": "(() => { const getId = a => { try { const u = new URL(a.href || a.getAttribute('href'), location.href); return u.searchParams.get('hotelId') || u.href; } catch(e) { return a.getAttribute('href') || ''; } }; let card = ROW; for (let i = 0; i < 10 && card.parentElement; i++) { const p = card.parentElement; const ids = new Set(Array.from(p.querySelectorAll('a[href*=\"/hotels/detail/\"],a[href*=\"/hotels/v2/detail/\"]')).map(getId).filter(Boolean)); const txt = p.innerText || ''; if (ids.size > 1 || txt.length > 3500 || /\\d[\\d,]*\\s+properties found/i.test(txt)) break; card = p; } const txt = card.innerText || ''; const m = txt.match(/(?:US|HK|SG|AU|CA|NZ)?\\$\\s?\\d[\\d,]*/i); return m ? m[0] : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Detail_url",
            "selector": "new URL(ROW.getAttribute('href') || ROW.href, location.href).href",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "total_nums",
            "selector": "(() => { const txt = document.body.innerText || ''; const m = txt.match(/\\d[\\d,]*\\s+properties found/i); return m ? m[0] : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "Current_Time",
            "selector": "new Date().toISOString()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "inject-javascript-1",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Execute JavaScript on the page",
      "position_x": 2304,
      "position_y": 800,
      "config": {
        "jsCode": "(() => new Promise(resolve => { const selector = 'a[href*=\"/hotels/detail/\"]:not([data-uscraper-exported]), a[href*=\"/hotels/v2/detail/\"]:not([data-uscraper-exported])'; const current = Array.from(document.querySelectorAll(selector)); current.forEach(a => { a.dataset.uscraperExported = 'true'; }); const getTargets = () => { const arr = [document.scrollingElement, document.documentElement, document.body, ...Array.from(document.querySelectorAll('main, section, div')).filter(e => e && e.scrollHeight > e.clientHeight + 120)]; return Array.from(new Set(arr.filter(Boolean))); }; let ticks = 0; const doScroll = () => { ticks += 1; const targets = getTargets(); targets.forEach(e => { try { e.scrollTop = Math.min(e.scrollTop + Math.max(900, Math.floor((e.clientHeight || 900) * 0.9)), e.scrollHeight); e.dispatchEvent(new WheelEvent('wheel', { bubbles: true, cancelable: true, deltaY: 1400 })); } catch (_) {} }); try { window.scrollBy(0, 1400); document.dispatchEvent(new WheelEvent('wheel', { bubbles: true, cancelable: true, deltaY: 1400 })); } catch (_) {} const fresh = document.querySelector(selector); if (fresh) { clearInterval(timer); resolve('new-links'); } if (ticks >= 19) { clearInterval(timer); resolve('no-new-links'); } }; const timer = setInterval(doScroll, 800); doScroll(); }))()",
        "waitForCompletion": true,
        "timeout": 25,
        "color": "bg-[#a56eff]"
      }
    },
    {
      "block_id": "sleep-2",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 2640,
      "position_y": 800,
      "config": {
        "duration": 1,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "element-exists-3",
      "block_type": "process",
      "title": "Element Exists",
      "description": "Check if element exists",
      "position_x": 2976,
      "position_y": 800,
      "config": {
        "selector": "a[href*=\"/hotels/detail/\"]:not([data-uscraper-exported]), a[href*=\"/hotels/v2/detail/\"]:not([data-uscraper-exported])",
        "color": "bg-[#ff832b]"
      }
    },
    {
      "block_id": "element-visible-4",
      "block_type": "process",
      "title": "Element Visible",
      "description": "Check if element is visible",
      "position_x": 3312,
      "position_y": 800,
      "config": {
        "selector": "//*[not(@disabled) and not(@aria-disabled='true') and (self::button or self::a or @role='button') and (contains(translate(normalize-space(.),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'load more') or contains(translate(normalize-space(.),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'more hotels') or contains(translate(normalize-space(.),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'more properties'))]",
        "color": "bg-[#ff832b]"
      }
    },
    {
      "block_id": "click-2",
      "block_type": "process",
      "title": "Click",
      "description": "Click on element",
      "position_x": 3648,
      "position_y": 800,
      "config": {
        "selector": "//*[not(@disabled) and not(@aria-disabled='true') and (self::button or self::a or @role='button') and (contains(translate(normalize-space(.),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'load more') or contains(translate(normalize-space(.),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'more hotels') or contains(translate(normalize-space(.),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'),'more properties'))]",
        "timeout": 15,
        "color": "bg-[#a56eff]"
      }
    },
    {
      "block_id": "sleep-4",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 3984,
      "position_y": 800,
      "config": {
        "duration": 4,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "element-visible-2",
      "block_type": "process",
      "title": "Element Visible",
      "description": "Check if element is visible",
      "position_x": 4320,
      "position_y": 800,
      "config": {
        "selector": "button[aria-label=\"Next\"]:not([disabled]), a[aria-label=\"Next\"]:not(.disabled), button[aria-label=\"Next page\"]:not([disabled]), a[aria-label=\"Next page\"]:not(.disabled), li[title=\"Next Page\"]:not([aria-disabled=\"true\"]), .pagination-next:not(.disabled), .ant-pagination-next:not(.ant-pagination-disabled)",
        "color": "bg-[#ff832b]"
      }
    },
    {
      "block_id": "click-1",
      "block_type": "process",
      "title": "Click",
      "description": "Click on element",
      "position_x": 4656,
      "position_y": 800,
      "config": {
        "selector": "button[aria-label=\"Next\"]:not([disabled]), a[aria-label=\"Next\"]:not(.disabled), button[aria-label=\"Next page\"]:not([disabled]), a[aria-label=\"Next page\"]:not(.disabled), li[title=\"Next Page\"]:not([aria-disabled=\"true\"]), .pagination-next:not(.disabled), .ant-pagination-next:not(.ant-pagination-disabled)",
        "timeout": 15,
        "color": "bg-[#a56eff]"
      }
    },
    {
      "block_id": "wait-for-page-load-2",
      "block_type": "process",
      "title": "Wait for Page Load",
      "description": "Wait for page to finish loading",
      "position_x": 4992,
      "position_y": 800,
      "config": {
        "timeout": 45,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "sleep-3",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 5328,
      "position_y": 800,
      "config": {
        "duration": 4,
        "color": "bg-[#08bdba]"
      }
    },
    {
      "block_id": "end-1",
      "block_type": "output",
      "title": "End",
      "description": "Terminate execution flow",
      "position_x": 5664,
      "position_y": 800,
      "config": {
        "color": "bg-[#8d8d8d]"
      }
    }
  ],
  "connections": [
    {
      "from_block_id": "set-window-size-1",
      "from_connector_id": "right",
      "to_block_id": "navigate-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "navigate-1",
      "from_connector_id": "right",
      "to_block_id": "wait-for-page-load-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-page-load-1",
      "from_connector_id": "right",
      "to_block_id": "sleep-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-1",
      "from_connector_id": "right",
      "to_block_id": "element-exists-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-exists-1",
      "from_connector_id": "true",
      "to_block_id": "end-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-exists-1",
      "from_connector_id": "false",
      "to_block_id": "wait-for-element-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-element-1",
      "from_connector_id": "right",
      "to_block_id": "structured-export-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "structured-export-1",
      "from_connector_id": "right",
      "to_block_id": "inject-javascript-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "inject-javascript-1",
      "from_connector_id": "right",
      "to_block_id": "sleep-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-2",
      "from_connector_id": "right",
      "to_block_id": "element-exists-3",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-exists-3",
      "from_connector_id": "true",
      "to_block_id": "structured-export-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-exists-3",
      "from_connector_id": "false",
      "to_block_id": "element-visible-4",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-visible-4",
      "from_connector_id": "true",
      "to_block_id": "click-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-visible-4",
      "from_connector_id": "false",
      "to_block_id": "element-visible-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "click-2",
      "from_connector_id": "right",
      "to_block_id": "sleep-4",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-4",
      "from_connector_id": "right",
      "to_block_id": "element-exists-3",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-visible-2",
      "from_connector_id": "true",
      "to_block_id": "click-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "element-visible-2",
      "from_connector_id": "false",
      "to_block_id": "end-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "click-1",
      "from_connector_id": "right",
      "to_block_id": "wait-for-page-load-2",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-page-load-2",
      "from_connector_id": "right",
      "to_block_id": "sleep-3",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-3",
      "from_connector_id": "right",
      "to_block_id": "wait-for-element-1",
      "to_connector_id": "left"
    }
  ],
  "canvas_elements": [
    {
      "id": "group-entry",
      "element_type": "group",
      "title": "Entry & Setup",
      "color": "#4589ff",
      "position_x": 48,
      "position_y": 116,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "set-window-size-1"
        ]
      }
    },
    {
      "id": "group-load",
      "element_type": "group",
      "title": "Page Load",
      "color": "#08bdba",
      "position_x": 384,
      "position_y": 116,
      "width": 5192,
      "height": 876,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "sleep-1",
          "wait-for-element-1",
          "sleep-2",
          "sleep-4",
          "wait-for-page-load-2",
          "sleep-3"
        ]
      }
    },
    {
      "id": "group-pagination",
      "element_type": "group",
      "title": "Pagination Loop",
      "color": "#ff832b",
      "position_x": 1392,
      "position_y": 116,
      "width": 3512,
      "height": 876,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "element-exists-1",
          "element-exists-3",
          "element-visible-4",
          "click-2",
          "element-visible-2",
          "click-1"
        ]
      }
    },
    {
      "id": "group-control",
      "element_type": "group",
      "title": "Control Flow",
      "color": "#8d8d8d",
      "position_x": 1728,
      "position_y": 416,
      "width": 4184,
      "height": 576,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "end-2",
          "end-1"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 1896,
      "position_y": 696,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "structured-export-1"
        ]
      }
    },
    {
      "id": "group-interaction",
      "element_type": "group",
      "title": "Interaction",
      "color": "#a56eff",
      "position_x": 2232,
      "position_y": 696,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "inject-javascript-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Extracts Trip.com hotel listing data and hotel detail URLs from a Trip.com hotel search/listing URL. Includes a sign-in guard because Trip.com may redirect anonymous sessions to sign-in. For accessible listing sessions, it exports unprocessed hotel detail links, marks them as exported, scrolls/wheels the page and scrollable containers while waiting for virtualized/lazy hotel links, optionally clicks listing-style Load more / More hotels / More properties controls, then checks visible result pagination. The fallback intentionally excludes generic 'Show More' buttons because Trip.com uses those inside hotel cards/modals.",
      "color": "#f1c21b",
      "position_x": 80,
      "position_y": 20,
      "width": 480,
      "height": 160,
      "z_index": 22,
      "data": {}
    },
    {
      "id": "note-block-element-exists-1",
      "element_type": "note",
      "title": "Note: Element Exists",
      "content": "Condition block: checks `#ibu_login_online input[placeholder*=\"email\"], form input[placeholder*=\"email\"], canvas.pb7GO`. True / False branches control which path runs next. Keep enough space between branches so both connector lines are visible.",
      "color": "#ee5396",
      "position_x": 1664,
      "position_y": 200,
      "width": 340,
      "height": 161,
      "z_index": 22,
      "data": {
        "block_id": "element-exists-1"
      }
    },
    {
      "id": "note-block-structured-export-1",
      "element_type": "note",
      "title": "Note: Structured Export",
      "content": "Structured export with JS columns (Web_Page_URL, Hotel_Name, Rating, Star_Rating, Number_of_Reviews). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 2168,
      "position_y": 780,
      "width": 340,
      "height": 135,
      "z_index": 22,
      "data": {
        "block_id": "structured-export-1"
      }
    },
    {
      "id": "note-block-inject-javascript-1",
      "element_type": "note",
      "title": "Note: Inject JavaScript",
      "content": "Runs custom JavaScript in the page: `(() => new Promise(resolve => { const selector = 'a[href*=\"/hotels/detail/\"]:not([data-uscraper-expo...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 2504,
      "position_y": 780,
      "width": 340,
      "height": 140,
      "z_index": 22,
      "data": {
        "block_id": "inject-javascript-1"
      }
    },
    {
      "id": "note-block-element-exists-3",
      "element_type": "note",
      "title": "Note: Element Exists",
      "content": "Condition block: checks `a[href*=\"/hotels/detail/\"]:not([data-uscraper-exported]), a[href*=\"/hotels/v2/detail/\"]:not([data-uscraper-exported])`. True / False branches control which path runs next. Keep enough space between branches so both connector lines are visible.",
      "color": "#ee5396",
      "position_x": 3176,
      "position_y": 780,
      "width": 340,
      "height": 169,
      "z_index": 22,
      "data": {
        "block_id": "element-exists-3"
      }
    },
    {
      "id": "note-block-element-visible-4",
      "element_type": "note",
      "title": "Note: Element Visible",
      "content": "Condition block: checks `//*[not(@disabled) and not(@aria-disabled='true') and (self::button or self::a or @role='button') and (contains(translat`. True / False branches control which path runs next. Keep enough space between branches so both connector lines are visible.",
      "color": "#ee5396",
      "position_x": 3512,
      "position_y": 780,
      "width": 340,
      "height": 170,
      "z_index": 22,
      "data": {
        "block_id": "element-visible-4"
      }
    },
    {
      "id": "note-block-element-visible-2",
      "element_type": "note",
      "title": "Note: Element Visible",
      "content": "Condition block: checks `button[aria-label=\"Next\"]:not([disabled]), a[aria-label=\"Next\"]:not(.disabled), button[aria-label=\"Next page\"]:not([disa`. True / False branches control which path runs next. Keep enough space between branches so both connector lines are visible.",
      "color": "#ee5396",
      "position_x": 4520,
      "position_y": 780,
      "width": 340,
      "height": 170,
      "z_index": 22,
      "data": {
        "block_id": "element-visible-2"
      }
    },
    {
      "id": "note-block-click-1",
      "element_type": "note",
      "title": "Note: Click",
      "content": "Pagination click — add waits after this block; the page reloads asynchronously.",
      "color": "#ee5396",
      "position_x": 4856,
      "position_y": 780,
      "width": 316,
      "height": 106,
      "z_index": 22,
      "data": {
        "block_id": "click-1"
      }
    }
  ]
}