Last active
February 14, 2022 17:35
-
-
Save w35l3y/db4dad924ca2e94871a4c9e5c8f012b3 to your computer and use it in GitHub Desktop.
Mantém e manipula requisições relacionadas ao ViewState
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Depends on | |
* https://gist.github.com/w35l3y/1d4e5578f7ce9d2a3d434729585defa6/ | |
* https://github.com/w35l3y/userscripts/raw/master-greasemonkey/includes/Includes_HttpRequest/ | |
*/ | |
function Session (errorFn) { | |
logger.info("Sessão iniciada."); | |
var dvs = document.querySelector("input[name = 'javax.faces.ViewState']"); | |
if (!dvs) { | |
throw "ViewState não encontrado ao requisitar página principal"; | |
} | |
var _this = this; | |
var actions = function (m, action, resolve, reject) { | |
var a = (action && "#" != action?(/^https?:/.test(action)?"":location.origin) + action:location.href); | |
logger.info("%s %s", m, a); | |
return { | |
method: m, | |
url: a, | |
onsuccess: function (xhr) { | |
var xml = xhr.response.xml; | |
logger.info("Document", xml); | |
var inputViewState = xml.querySelector("#javax\\.faces\\.ViewState,input[name = 'javax.faces.ViewState']"); | |
logger.info("Input ViewState", inputViewState); | |
var errors = errorFn(xml); | |
if (inputViewState) { | |
_this.viewState = inputViewState.value || inputViewState.textContent; | |
logger.info("ViewState", _this.viewState); | |
} else { | |
logger.error("Erro", errors, [m, action], a, xml, xhr.response.raw); | |
reject(xml, _this); | |
return | |
} | |
if (0 < errors.length) { | |
logger.error("Erro", errors, [m, action], a, xml, xhr.response.raw); | |
reject(xml, _this); | |
} else { | |
logger.info("Página retornada"); | |
resolve(xml, _this); | |
} | |
}, | |
onfail: function () { | |
logger.info("Fail", arguments); | |
reject(xhr.response.xml, _this); | |
} | |
}; | |
}; | |
this.clone = function () { | |
var ses = new Session(errorFn); | |
ses.viewState = _this.viewState; | |
return ses; | |
}; | |
this.viewState = dvs.value; | |
this.update = function (doc) { | |
logger.warn("Atualizando ViewState do documento...", doc); | |
doc.querySelectorAll("#javax\\.faces\\.ViewState,input[name = 'javax.faces.ViewState']").forEach(function (vs) { | |
vs.value = _this.viewState; | |
}); | |
}; | |
this.submit = function (action, sync, event) { | |
if (!action) { | |
logger.info("Ação não encontrada na página."); | |
return Promise.reject(null, _this); | |
} | |
var d = action.ownerDocument; | |
logger.info("Ação identificada.", action); | |
logger.info("Verificando documento da ação informada...", d === document); | |
if (d === document) { // current page | |
logger.info("Verificando sincronicidade do evento..."); | |
if (sync) { | |
action.click(); | |
logger.info("Evento CLICK executado com sucesso.");; | |
return Promise.resolve(d, _this); | |
} | |
} | |
_this.update(d); | |
var form = action.form; | |
var params = []; | |
var partialUpdate = false; | |
logger.info("Verificando tipo de ação assincrona..."); | |
if (action instanceof HTMLAnchorElement && "#" == action.getAttribute("href") && /'([^']+)'\),{'([^']+)'/.test(action.getAttribute("onclick") || action.getAttribute("data-onclick"))) { | |
form = d.getElementById(RegExp.$1); | |
action = d.createElement("input"); | |
action.setAttribute("name", RegExp.$2); | |
action.setAttribute("value", RegExp.$2); | |
logger.info("Criada ação FAKE para simular o envio de formulário...", action, RegExp.$1, RegExp.$2); | |
} else if (action instanceof HTMLAnchorElement && /^#((\w+):\w+)/.test(action.getAttribute("href"))) { | |
if (!event) { | |
logger.error("Ausência de evento reconhecido...", action); | |
return Promise.reject(d); | |
} | |
var containerId = RegExp.$2; | |
logger.info("Container...", containerId) | |
var container = d.getElementById(containerId); | |
form = container.querySelector("form"); | |
params.push({name:"javax.faces.partial.execute", value: containerId}); | |
params.push({name:"javax.faces.partial.render", value: containerId}); | |
params.push({name:"javax.faces.source", value: containerId}); | |
params.push({name:"javax.faces.partial.event", value: event}); | |
params.push({name:"javax.faces.behavior.event", value: event}); | |
if ("tabChange" == event) { | |
params.push({name:containerId + "_contentLoad", value: "true"}); | |
params.push({name:containerId + "_newTab", value: RegExp.$1}); | |
params.push({name:containerId + "_tabindex", value: xpath("./ancestor::ul[1]/li", action).reduce(function (s, v, i) {return null !== s?s:(v == action.parentNode?i:null)}, null)}); | |
} | |
partialUpdate = { | |
id : containerId, | |
event : event, | |
action : action, | |
container: d.querySelector("#" + escapeRegex(RegExp.$1)) | |
}; | |
action = d.createElement("input"); | |
action.setAttribute("name", "javax.faces.partial.ajax"); | |
action.setAttribute("value", "true"); | |
} else if (event) { | |
logger.error("Evento em ACTION não reconhecida.", action, event); | |
return Promise.reject(d); | |
} | |
logger.info("Parâmetros adicionais à requisição...", params); | |
if (form instanceof HTMLFormElement) { | |
logger.info("Enviando formulário...", form); | |
return new Promise(function (resolve, reject) { | |
var fields = Array.prototype.slice.apply(form.elements).filter(function (field) { | |
return !(field instanceof HTMLButtonElement) && !(field instanceof HTMLFieldSetElement); | |
}); | |
Array.prototype.push.apply(fields, params); | |
logger.info("Verificando existência de campo..."); | |
if (-1 == fields.indexOf(action)) { | |
logger.info("Campo adicionado.", action); | |
fields.push(action); | |
} | |
var openData = actions(form.method.toUpperCase(), form.action, function (xml) { | |
if (!partialUpdate) { | |
return resolve(xml); | |
} | |
var viewStateTmp = null; | |
xpath(".//changes/update[@id]", xml).forEach(function (update) { | |
var id = update.getAttribute("id"); | |
logger.warn("Atualização parcial...", id, update); | |
switch (id) { | |
case "javax.faces.ViewState": // Altera value do campo | |
Array.prototype.slice.apply(d.querySelectorAll("#javax\\.faces\\.ViewState,input[name = 'javax.faces.ViewState']")).forEach(function (element) { | |
logger.warn("Atualizando ViewState...", element, update); | |
viewStateTmp = update.textContent; | |
element.value = update.textContent; | |
element.setAttribute("value", update.textContent); | |
}); | |
break; | |
case partialUpdate.container.id: // Altera o container | |
logger.warn("Sobreescrevendo container...", d, partialUpdate, update); | |
d.getElementById(partialUpdate.id).innerHTML = update.textContent; | |
break; | |
default: // Sobreescreve o elemento | |
Array.prototype.slice.apply(d.querySelectorAll("#" + escapeRegex(update.getAttribute("id")))).forEach(function (element) { | |
logger.warn("Sobreescrevendo elemento...", element, update); | |
element.outerHTML = update.textContent; | |
}); | |
break; | |
} | |
}); | |
var inputViewState = document.createElement("input"); | |
inputViewState.setAttribute("id", "javax.faces.ViewState"); | |
inputViewState.setAttribute("name", "javax.faces.ViewState"); | |
inputViewState.setAttribute("autocomplete", "off"); | |
inputViewState.setAttribute("type", "hidden"); | |
// @TODO Verificar se estas linhas são mesmo necessárias ou se basta incluir o input vazio já que o update é feito antes das requisições | |
inputViewState.value = viewStateTmp; | |
inputViewState.setAttribute("value", viewStateTmp); | |
logger.warn("Inserindo ViewState nos forms do container...", inputViewState); | |
xpath("form", d.getElementById(partialUpdate.id)).forEach(function (f) { | |
logger.warn("ViewState inserido...", f); | |
f.insertBefore(inputViewState.cloneNode(true), f.firstElementChild); | |
}); | |
executePartialAction(partialUpdate); | |
resolve(d); | |
}, reject); | |
logger.info("Informações do formulário a serem enviadas...", fields, form, resolve); | |
logger.warn("Requisição FORM...", openData, fields); | |
HttpRequest.open(openData).send(fields); | |
}); | |
} | |
logger.info("Acessando link...", action); | |
return new Promise(function (resolve, reject) { | |
var openData = actions("GET", action.getAttribute("href"), resolve, reject); | |
var sendData = params.reduce(function (s, v) { | |
s[v.name] = v.value; | |
return s; | |
}, {}); | |
logger.warn("Requisição LINK...", openData, sendData); | |
HttpRequest.open(openData).send(sendData); | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment