///|
fn Browser::set_text_control_selection_from_cells(
  self : Browser,
  source_id : String,
  anchor_col : Int,
  anchor_row : Int,
  focus_col : Int,
  focus_row : Int,
) -> Bool {
  if !self.enable_js {
    return false
  }
  match self.get_hit_region_for_source_id(source_id) {
    Some(region) => {
      let anchor_local_col = if anchor_col <= region.col {
        0
      } else if anchor_col >= region.col + region.width {
        region.width
      } else {
        anchor_col - region.col
      }
      let anchor_local_row = if anchor_row <= region.row {
        0
      } else if anchor_row >= region.row + region.height {
        region.height - 1
      } else {
        anchor_row - region.row
      }
      let focus_local_col = if focus_col <= region.col {
        0
      } else if focus_col >= region.col + region.width {
        region.width
      } else {
        focus_col - region.col
      }
      let focus_local_row = if focus_row <= region.row {
        0
      } else if focus_row >= region.row + region.height {
        region.height - 1
      } else {
        focus_row - region.row
      }
      let escaped = escape_js_string(source_id)
      let source = "(function(){const target=document.getElementById('" +
        escaped +
        "');if(!target){return 'missing';}const tag=(target.tagName||'').toLowerCase();const type=(target.getAttribute?((target.getAttribute('type')||'').toLowerCase()):'');function isTextLikeInput(){if(tag!=='input'){return false;}return type===''||type==='text'||type==='search'||type==='url'||type==='tel'||type==='email'||type==='password'||type==='number';}function currentValue(el){if(!el){return '';}if(el.value!==undefined&&el.value!==null){return String(el.value);}if(el.getAttribute){return String(el.getAttribute('value')||'');}return '';}function setCaret(index){const value=currentValue(target);const clamped=Math.max(0,Math.min(value.length,Number(index)||0));if(typeof target.setSelectionRange==='function'){target.setSelectionRange(clamped,clamped);}else{target.selectionStart=clamped;target.selectionEnd=clamped;}return 'ok';}if(tag==='textarea'){const value=currentValue(target);const lines=value.split('\\n');const localRow=" +
        anchor_local_row.to_string() +
        ";const anchorCol=" +
        anchor_local_col.to_string() +
        ";const focusRow=" +
        focus_local_row.to_string() +
        ";const focusCol=" +
        focus_local_col.to_string() +
        ";const anchorRow=localRow;function indexForPosition(row,col){let offset=0;for(let i=0;i=row||i===lines.length-1){return offset+Math.max(0,Math.min(line.length,col));}offset+=line.length+1;}return value.length;}const anchor=indexForPosition(anchorRow,anchorCol);const focus=indexForPosition(focusRow,focusCol);const start=Math.min(anchor,focus);const end=Math.max(anchor,focus);if(typeof target.setSelectionRange==='function'){target.setSelectionRange(start,end);}else{target.selectionStart=start;target.selectionEnd=end;}return 'ok';}if(isTextLikeInput()){const value=currentValue(target);const anchor=Math.max(0,Math.min(value.length," +
        anchor_local_col.to_string() +
        "));const focus=Math.max(0,Math.min(value.length," +
        focus_local_col.to_string() +
        "));const start=Math.min(anchor,focus);const end=Math.max(anchor,focus);if(typeof target.setSelectionRange==='function'){target.setSelectionRange(start,end);}else{target.selectionStart=start;target.selectionEnd=end;}return 'ok';}return 'none';})()"
      self.execute_inline_js(source) == Some("ok")
    }
    None => false
  }
}

///|
fn Browser::set_text_control_caret_from_cell(
  self : Browser,
  source_id : String,
  col : Int,
  row : Int,
) -> Bool {
  self.set_text_control_selection_from_cells(source_id, col, row, col, row)
}

///|
fn Browser::dispatch_focused_key_to_source_id(
  self : Browser,
  source_id : String,
  key : String,
) -> FocusedKeyDispatchResult {
  if !self.enable_js {
    return NotHandled
  }
  let escaped_id = escape_js_string(source_id)
  let escaped_key = escape_js_string(key)
  let source = "(function(){const target=document.getElementById('" +
    escaped_id +
    "');const key='" +
    escaped_key +
    "';if(!target){return 'none';}const tag=(target.tagName||'').toLowerCase();const type=(target.getAttribute?((target.getAttribute('type')||'').toLowerCase()):'');function dispatchKeyboard(typeName,cancelable){const ev=new KeyboardEvent(typeName,{bubbles:true,cancelable,key:key});return target.dispatchEvent(ev);}function finishHandled(){dispatchKeyboard('keyup',false);return 'handled';}function finish(result){dispatchKeyboard('keyup',false);return result;}function dispatchBeforeInput(inputType,data){let event;if(typeof InputEvent==='function'){event=new InputEvent('beforeinput',{bubbles:true,cancelable:true,data,inputType});}else{event=new Event('beforeinput',{bubbles:true,cancelable:true});event.data=data;event.inputType=inputType;}return target.dispatchEvent(event);}function dispatchInput(){target.dispatchEvent(new Event('input',{bubbles:true}));}function dispatchChange(){target.dispatchEvent(new Event('change',{bubbles:true}));markChangeBase(target);}function currentValue(el){if(!el){return '';}if(el.value!==undefined&&el.value!==null){return String(el.value);}if(el.getAttribute){return String(el.getAttribute('value')||'');}return '';}function markChangeBase(el){if(!el||!el.setAttribute){return;}el.setAttribute('data-crater-change-base',currentValue(el));}function ensureDefaultValue(el){if(!el||!el.getAttribute||!el.setAttribute){return;}if(el.getAttribute('data-crater-default-value')!==null){return;}el.setAttribute('data-crater-default-value',currentValue(el));}function ensureDefaultChecked(el){if(!el||!el.getAttribute||!el.setAttribute){return;}if(el.getAttribute('data-crater-default-checked')!==null){return;}el.setAttribute('data-crater-default-checked',el.checked?'true':'false');}function syncValueAttr(el){if(el&&el.setAttribute){el.setAttribute('value',currentValue(el));}}function syncCheckedAttr(el){if(!el||!el.setAttribute){return;}if(el.checked){el.setAttribute('checked','');}else if(el.removeAttribute){el.removeAttribute('checked');}}function currentSelection(){const value=currentValue(target);const length=value.length;let start=target.selectionStart;let end=target.selectionEnd;if(start===undefined||start===null){start=length;}if(end===undefined||end===null){end=start;}start=Math.max(0,Math.min(length,Number(start)||0));end=Math.max(0,Math.min(length,Number(end)||0));if(end {
      let _ = self.sync_render_state_from_dom_tree()
      match result {
        "handled" => Handled
        "activate" => Activate
        "submit" => Submit
        _ => NotHandled
      }
    }
    None => NotHandled
  }
}