Skip to content

Instantly share code, notes, and snippets.

@w35l3y
Last active February 14, 2022 17:35
Show Gist options
  • Save w35l3y/db4dad924ca2e94871a4c9e5c8f012b3 to your computer and use it in GitHub Desktop.
Save w35l3y/db4dad924ca2e94871a4c9e5c8f012b3 to your computer and use it in GitHub Desktop.
Mantém e manipula requisições relacionadas ao ViewState
/**
* 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