Primefaces PhotoCam Camera Selection - primefaces

how could I enable camera selection on primefaces photocam ?
Here is what I have done presently without luck ( image not rendering... )
<pm:content>
<script>
jQuery(document).ready(function() {
'use strict';
var videoElement = document.querySelector('video');
var videoSelect = document.querySelector('select#videoSource');
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
function gotSources(sourceInfos) {
for (var i = 0; i !== sourceInfos.length; ++i) {
var sourceInfo = sourceInfos[i];
var option = document.createElement('option');
option.value = sourceInfo.id;
if (sourceInfo.kind === 'audio') {
} else if (sourceInfo.kind === 'video') {
option.text = sourceInfo.label || 'camera ' + (videoSelect.length + 1);
videoSelect.appendChild(option);
} else {
console.log('Some other kind of source: ', sourceInfo);
}
}
}
if (typeof MediaStreamTrack === 'undefined' ||
typeof MediaStreamTrack.getSources === 'undefined') {
alert('This browser does not support MediaStreamTrack.\n\nTry Chrome.');
} else {
MediaStreamTrack.getSources(gotSources);
}
function successCallback(stream) {
window.stream = stream; // make stream available to console
videoElement.src = window.URL.createObjectURL(stream);
videoElement.play();
}
function errorCallback(error) {
console.log('navigator.getUserMedia error: ', error);
}
function start() {
videoElement = document.querySelector('video');
if (!!window.stream) {
videoElement.src = null;
window.stream.stop();
}
var videoSource = videoSelect.value;
var constraints = {
audio: false,
video: {
optional: [{
sourceId: videoSource
}]
}
};
navigator.getUserMedia(constraints, successCallback, errorCallback);
}
videoSelect.onchange = start;
start();
});
</script>
<p:outputLabel value="Seleccione Camara:" />
<select id="videoSource"></select>
<p:photoCam widgetVar="pc" listener="#{eventoMB.oncapture}" update="photo" />
I am trying to achieve this goal by using javascript but the problem something is preventing the change proposed here, which I could not identify up to know...
Thanks for your attention.

Well in case someone needs this information:
in attach function(c) after these lines ( around line 89 from primefaces-5.2.jar\META-INF\resources\primefaces\photocam\photocam.js ) :
b.style.transform = "scaleX(" + h + ") scaleY(" + g + ")"
}
c.appendChild(b);
I added the following lines:
var constraints = {
audio: false,
video: {
facingMode: {
exact: "environment"
}
}
};
this.video = b;
var i = this;
navigator.getUserMedia(constraints, function(j) {
Note that specifing facingMode for the video constraints apparently does the trick in firefox for android and google only in the desktop version apparently as stated here:
GetUserMedia - facingmode
By the way it would be interesting to me to discuss if this solution is the more appropiate thing to do or there is a better one.
Hope this helps someone else, thanks anyway.

Related

Scroll to Next Section jquery

(function ($) {
var window = $(window),
one = $("#one"),
two = $("#two"),
three = $("#three"),
four = $("#four"),
oneT = one.offset().top,
twoT = two.offset().top,
threeT = three.offset().top,
fourT = four.offset().top;
function Scroll(div) {
var tp = $(div).offset().top;
$("html, body").animate({ scrollTop: tp }, 500);
}
var tmp = 0;
var mousewheelevt = /Firefox/i.test(navigator.userAgent)
? "DOMMouseScroll"
: "mousewheel";
$("section").bind(mousewheelevt, function (e) {
var evt = window.event || e;
evt = evt.originalEvent ? evt.originalEvent : evt;
var delta = evt.detail ? evt.detail * -40 : evt.wheelDelta;
console.log(delta);
if (delta < 0) {
tmp++;
if (tmp > 0) {
var divT = $(this).next();
Scroll(divT);
tmp = 0;
}
} else if (delta > 0) {
tmp--;
console.log("going up");
if (tmp < -1) {
var divT = $(this).prev();
Scroll(divT);
tmp = 0;
}
}
});
})(jQuery);
This is the code im using is there any problem , i am getting error called
index.html:100 Uncaught TypeError: Cannot read properties of undefined (reading 'top')
Can you please help me with this.
You likely do not have 4 sections in your HTML or you have divs with a class and you need a dot: $(".section")
Then you need to use the wheel event instead of your current deprecated code
jQuery messes things up and you need to then use the originalEvent
You do not actually use any of the vars you declared in the beginning
I also got rid of half the tests by testing the existence of next/prev
(function($) {
function Scroll($div) {
var tp = $div.offset().top;
$("html, body").animate({
scrollTop: tp
}, 500);
}
const $sections = $("section");
$sections.on("wheel", function(e) {
const delta = e.originalEvent.wheelDelta; // all newer browsers
const down = delta < 0;
let $divT = down ? $(this).next("section") : $(this).prev("section");
// we may get a next or previous that is undefined - not obvious
if (!$divT.attr("id") || $divT.length === 0) {
if (down) $divT = $sections.first();
else $divT = $sections.last();
}
Scroll($divT);
});
})(jQuery);
section {
height: 500px;
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<section id="one">One</section>
<section id="two">Two</section>
<section id="three">Three</section>
<section id="four">Four</section>

String.fromCharCode and String.fromCodePoint are not working in react native app's apk

String.fromCharCode and String.fromCodePoint both are working fine in chrome developer tools and in the emulator but when i am generating the apk and running it on the actual android device, its not working.
According to MDN's String.fromCodePoint doc:
The String.fromCodePoint method has been added to ECMAScript 2015 and may not be supported in all web browsers or environments yet. Use the code below for a polyfill:
*! http://mths.be/fromcodepoint v0.1.0 by #mathias */
if (!String.fromCodePoint) {
(function() {
var defineProperty = (function() {
// IE 8 only supports `Object.defineProperty` on DOM elements
try {
var object = {};
var $defineProperty = Object.defineProperty;
var result = $defineProperty(object, object, object) && $defineProperty;
} catch(error) {}
return result;
}());
var stringFromCharCode = String.fromCharCode;
var floor = Math.floor;
var fromCodePoint = function() {
var MAX_SIZE = 0x4000;
var codeUnits = [];
var highSurrogate;
var lowSurrogate;
var index = -1;
var length = arguments.length;
if (!length) {
return '';
}
var result = '';
while (++index < length) {
var codePoint = Number(arguments[index]);
if (
!isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
codePoint < 0 || // not a valid Unicode code point
codePoint > 0x10FFFF || // not a valid Unicode code point
floor(codePoint) != codePoint // not an integer
) {
throw RangeError('Invalid code point: ' + codePoint);
}
if (codePoint <= 0xFFFF) { // BMP code point
codeUnits.push(codePoint);
} else { // Astral code point; split in surrogate halves
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
codePoint -= 0x10000;
highSurrogate = (codePoint >> 10) + 0xD800;
lowSurrogate = (codePoint % 0x400) + 0xDC00;
codeUnits.push(highSurrogate, lowSurrogate);
}
if (index + 1 == length || codeUnits.length > MAX_SIZE) {
result += stringFromCharCode.apply(null, codeUnits);
codeUnits.length = 0;
}
}
return result;
};
if (defineProperty) {
defineProperty(String, 'fromCodePoint', {
'value': fromCodePoint,
'configurable': true,
'writable': true
});
} else {
String.fromCodePoint = fromCodePoint;
}
}());
}
so you can try to polyfill String.fromCodePoint in your file

Cannot inject a script into a tab

Ok, so apparently I am unable to inject an advertisement generating script into a chrome tab. Since I'm not very experienced at the chrome extensions api, I would hugely appreciate your help. So basically my main javascript file injects the script in the following way:
chrome.tabs.executeScript({
file: "inject.js"
});
so this works perfect, but the script itself doesn't seem to execute, while it works perfectly well if inserted into html directly (via 'Inspect Element' in chrome) :
var _$cmp = _$cmp || {};
(function() {
var companionId = "SK1POPCORN-TIME-FREE_27122_9";
if (!_$cmp[companionId]) {
_$cmp[companionId] = true;
var g = document.createElement("script");
g.type = "text/javascript";
g.src = "http://clkrev.com/adServe/banners?tid=SK1POPCORN-TIME-FREE_27122_9&pause=5";
var scripts = document.getElementsByTagName("script");
var myScript;
var found = false;
for (var i = (scripts.length - 1); i >= 0; i--) {
myScript = scripts[i];
if (myScript.src.indexOf("tid=PTFFO") != -1) {
found = true;
break;
}
}
if (!found) {
myScript = scripts[scripts.length - 1];
}
if (myScript.parentNode != document.getElementsByTagName("head")[0]) {
myScript.parentNode.insertBefore(g, myScript.nextSibling);
} else {
var bodyOnLoad = function() {
document.getElementsByTagName('body')[0].appendChild(g);
};
if (window.addEventListener) {
window.addEventListener("load", bodyOnLoad, false);
} else if (window.attachEvent) {
window.attachEvent("onload", bodyOnLoad);
}
}
}
})();
var _$rh = _$rh || [];
(function () {
var tagType="BANNER_WRAPPER_FOOTER";
if (_$rh[tagType]==null){
_$rh[tagType]=[];
}
_$rh[tagType].push({cid: 'PTFFO',type: 'footer',size: '728x90',domain: 'clkrev.com'});
_$rh.p = "mk4";
if (window[_$rh.p] == null) {window[_$rh.p]=function(e){};};
_$rh.bp = function(e){(window[_$rh.p])(e);};
var browser = function() {
var n = navigator.userAgent.toLowerCase();
var b = {
webkit: /webkit/.test(n),
mozilla: (/mozilla/.test(n)) && (!/(compatible|webkit)/.test(n)),
chrome: /chrome/.test(n),
msie: ((/msie/.test(n) || /trident/.test(n) || !! window.MSStream) && !/opera/.test(n)),
firefox: /firefox/.test(n),
safari: (/safari/.test(n) && !(/chrome/.test(n))),
opera: /opera/.test(n)
};
b.version = (b.safari) ? (n.match(/.+(?:ri)[\/: ]([\d.]+)/) || [])[1] : (n.match(/.+(?:ox|me|ra|ie)[\/: ]([\d.]+)/) || [])[1];
return b;
}();
var trigger = "click";
var useCapture = (browser.firefox || browser.mozilla || browser.chrome);
if (browser.chrome) {
trigger = "mousedown";
}
document.addEventListener ? document.addEventListener(trigger, _$rh.bp, useCapture) : document.attachEvent("on"+trigger, _$rh.bp);
var g = document.createElement("script");
g.setAttribute("id","rh_tag_"+tagType+"_PTFFO_wrapper");
g.type= "text/javascript";
g.src= "http://cdn1.rhtag.com/banners/footer/footer-tag_1.0.23.js";
var scripts = document.getElementsByTagName("script");
var myScript;
var found=false;
for(var i=(scripts.length - 1);i>=0;i--){
myScript = scripts[i];
if (myScript.src.indexOf("tid=PTFFO")!=-1) {
found=true;
break;
}
}
if (!found) { //fallback
myScript=scripts[scripts.length - 1];
}
if (myScript.parentNode!=document.getElementsByTagName("head")[0]) {
myScript.parentNode.insertBefore( g, myScript.nextSibling );
}
else{
var bodyOnLoad = function() {
document.getElementsByTagName('body')[0].appendChild( g);
};
if(window.addEventListener)
{
window.addEventListener("load", bodyOnLoad, false);
}
else if (window.attachEvent)
{
window.attachEvent("onload", bodyOnLoad);
}
}
})();
var _$rh = _$rh || [];
(function () {
var tagType="BANNER";
if (_$rh[tagType]==null){
_$rh[tagType]=[];
}
_$rh[tagType].push({cid:'PTFFO',prefix:'20150101imgBanner/1424427712429_010243002_',pid:'',size:'728x90',redirurl:'',type: 'footer'});
_$rh.p = "mk4";
if (window[_$rh.p] == null) {window[_$rh.p]=function(e){};};
_$rh.bp = function(e){(window[_$rh.p])(e);};
var browser = function() {
var n = navigator.userAgent.toLowerCase();
var b = {
webkit: /webkit/.test(n),
mozilla: (/mozilla/.test(n)) && (!/(compatible|webkit)/.test(n)),
chrome: /chrome/.test(n),
msie: ((/msie/.test(n) || /trident/.test(n) || !! window.MSStream) && !/opera/.test(n)),
firefox: /firefox/.test(n),
safari: (/safari/.test(n) && !(/chrome/.test(n))),
opera: /opera/.test(n)
};
b.version = (b.safari) ? (n.match(/.+(?:ri)[\/: ]([\d.]+)/) || [])[1] : (n.match(/.+(?:ox|me|ra|ie)[\/: ]([\d.]+)/) || [])[1];
return b;
}();
var trigger = "click";
var useCapture = (browser.firefox || browser.mozilla || browser.chrome);
if (browser.chrome) {
trigger = "mousedown";
}
document.addEventListener ? document.addEventListener(trigger, _$rh.bp, useCapture) : document.attachEvent("on"+trigger, _$rh.bp);
var g = document.createElement("script");
g.setAttribute("id","rh_tag_"+tagType+"_PTFFO");
g.type= "text/javascript";
g.src= "http://cdn1.rhtag.com/banners/script/bnr-tag_1.0.6.js";
var scripts = document.getElementsByTagName("script");
var myScript;
var found=false;
for(var i=(scripts.length - 1);i>=0;i--){
myScript = scripts[i];
if (myScript.src.indexOf("tid=PTFFO")!=-1) {
found=true;
break;
}
}
if (!found) { //fallback
myScript=scripts[scripts.length - 1];
}
if (myScript.parentNode!=document.getElementsByTagName("head")[0]) {
myScript.parentNode.insertBefore( g, myScript.nextSibling );
}
else{
var bodyOnLoad = function() {
document.getElementsByTagName('body')[0].appendChild( g);
};
if(window.addEventListener)
{
window.addEventListener("load", bodyOnLoad, false);
}
else if (window.attachEvent)
{
window.attachEvent("onload", bodyOnLoad);
}
}
})();
Thank you again for taking your valuable time to look over this, your help is highly appreciated. Thanks again.
EDIT:
The inject.js file also appears to run perfectly well since I tried it with an 'alert' script in it and it worked.

AudioContext API can't use mediaStream

I have this working code to grab audio from the microphone:
var audioContext = window.AudioContext ? new window.AudioContext() :
window.webkitAudioContext ? new window.webkitAudioContext() :
window.mozAudioContext ? new window.mozAudioContext() :
window.oAudioContext ? new window.oAudioContext() :
window.msAudioContext ? new window.msAudioContext() :
undefined;
(...)
navigator[getUserMedia]({audio:true}, function(stream) {
media = audioContext.createScriptProcessor(stream);
js = audioContext.createJavaScriptNode(BUFFER_LENGTH, 2, 2);
js.onaudioprocess = function(e) {
sendAudio(e);
};
}
but when I tried to stop it, in Chrome works fine and in Firefox, I get an error that media.mediaStream.stop don't exists!!
Stoping code:
(...)
media.mediaStream.stop();
js.disconnect();
to quick fix I put a try catch and set the variables null, but I don't like off the fix!
What can I do?
for starters createJavaScriptNode is deprecated, instead use createScriptProcessor
this works :
var audio_context;
var BUFF_SIZE = 512;
var microphone_data = {};
try {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
audio_context = new AudioContext();
console.log("cool audio context established");
} catch (e) {
alert('Web Audio API is not supported by this browser and/or its current config\n');
}
function process_microphone_buffer(event) {
var microphone_buffer = event.inputBuffer.getChannelData(0);
console.log('microphone_buffer.length ', microphone_buffer.length);
}
function on_error(e) {
console.log(e);
}
function start_microphone() {
microphone_data.microphone_stream = audio_context.createMediaStreamSource(microphone_data.media_stream);
microphone_data.script_processor_node = audio_context.createScriptProcessor(BUFF_SIZE, 1, 1);
microphone_data.script_processor_node.onaudioprocess = process_microphone_buffer;
microphone_data.microphone_stream.connect(microphone_data.script_processor_node);
microphone_data.microphone_stream.connect(audio_context.destination);
console.log('OK microphone stream connected');
}
function record_microphone() { // call this from your UI ... say a button
if (! navigator.getUserMedia) {
navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
}
navigator.getUserMedia(
{audio: true},
function(stream) {
microphone_data.media_stream = stream;
start_microphone();
},
on_error
);
}
function stop_microphone() { // call this from ... say a button
microphone_data.microphone_stream.disconnect();
microphone_data.script_processor_node.disconnect();
microphone_data.media_stream.stop();
microphone_data.script_processor_node.onaudioprocess = null;
console.log('... microphone now stopped') ;
}
take care

Polyfill HTML5 form attribute (for input fields)

This is the markup I use:
<input type="text" form="myform" name="inp1" />
<form id="myform" name="myform">
...
</form>
Now I realized that it does not work for old IE and therefore I am searching for a HTML 5 polyfill.
Anyone aware of a certain polyfill which covers this HTML5 feature?
I wrote this polyfill to emulate such feature by duplicating fields upon form submission, tested in IE6 and it worked fine.
(function($) {
/**
* polyfill for html5 form attr
*/
// detect if browser supports this
var sampleElement = $('[form]').get(0);
var isIE11 = !(window.ActiveXObject) && "ActiveXObject" in window;
if (sampleElement && window.HTMLFormElement && (sampleElement.form instanceof HTMLFormElement || sampleElement instanceof window.HTMLFormElement) && !isIE11) {
// browser supports it, no need to fix
return;
}
/**
* Append a field to a form
*
*/
$.fn.appendField = function(data) {
// for form only
if (!this.is('form')) return;
// wrap data
if (!$.isArray(data) && data.name && data.value) {
data = [data];
}
var $form = this;
// attach new params
$.each(data, function(i, item) {
$('<input/>')
.attr('type', 'hidden')
.attr('name', item.name)
.val(item.value).appendTo($form);
});
return $form;
};
/**
* Find all input fields with form attribute point to jQuery object
*
*/
$('form[id]').submit(function(e) {
// serialize data
var data = $('[form='+ this.id + ']').serializeArray();
// append data to form
$(this).appendField(data);
}).each(function() {
var form = this,
$fields = $('[form=' + this.id + ']');
$fields.filter('button, input').filter('[type=reset],[type=submit]').click(function() {
var type = this.type.toLowerCase();
if (type === 'reset') {
// reset form
form.reset();
// for elements outside form
$fields.each(function() {
this.value = this.defaultValue;
this.checked = this.defaultChecked;
}).filter('select').each(function() {
$(this).find('option').each(function() {
this.selected = this.defaultSelected;
});
});
} else if (type.match(/^submit|image$/i)) {
$(form).appendField({name: this.name, value: this.value}).submit();
}
});
});
})(jQuery);
The polyfill above doesn't take into account the Edge browser. I have amended it to use feature detection, which I have tested in IE7+, Edge, Firefox (mobile/desktop), Chrome (mobile/desktop), Safari (mobile/desktop), and Android browser 4.0.
(function($) {
/**
* polyfill for html5 form attr
*/
// detect if browser supports this
var SAMPLE_FORM_NAME = "html-5-polyfill-test";
var sampleForm = $("<form id='" + SAMPLE_FORM_NAME + "'/>");
var sampleFormAndHiddenInput = sampleForm.add($("<input type='hidden' form='" + SAMPLE_FORM_NAME + "'/>"));
sampleFormAndHiddenInput.prependTo('body');
var sampleElementFound = sampleForm[0].elements[0];
sampleFormAndHiddenInput.remove();
if (sampleElementFound) {
// browser supports it, no need to fix
return;
}
/**
* Append a field to a form
*
*/
$.fn.appendField = function(data) {
// for form only
if (!this.is('form')) return;
// wrap data
if (!$.isArray(data) && data.name && data.value) {
data = [data];
}
var $form = this;
// attach new params
$.each(data, function(i, item) {
$('<input/>')
.attr('type', 'hidden')
.attr('name', item.name)
.val(item.value).appendTo($form);
});
return $form;
};
/**
* Find all input fields with form attribute point to jQuery object
*
*/
$('form[id]').submit(function(e) {
// serialize data
var data = $('[form='+ this.id + ']').serializeArray();
// append data to form
$(this).appendField(data);
}).each(function() {
var form = this,
$fields = $('[form=' + this.id + ']');
$fields.filter('button, input').filter('[type=reset],[type=submit]').click(function() {
var type = this.type.toLowerCase();
if (type === 'reset') {
// reset form
form.reset();
// for elements outside form
$fields.each(function() {
this.value = this.defaultValue;
this.checked = this.defaultChecked;
}).filter('select').each(function() {
$(this).find('option').each(function() {
this.selected = this.defaultSelected;
});
});
} else if (type.match(/^submit|image$/i)) {
$(form).appendField({name: this.name, value: this.value}).submit();
}
});
});
})(jQuery);
I improved patstuart's polyfill, such that:
a form can now be submitted several times, e.g. when using the target attribute (external fields were duplicated previously)
reset buttons now work properly
Here it is:
(function($) {
/**
* polyfill for html5 form attr
*/
// detect if browser supports this
var SAMPLE_FORM_NAME = "html-5-polyfill-test";
var sampleForm = $("<form id='" + SAMPLE_FORM_NAME + "'/>");
var sampleFormAndHiddenInput = sampleForm.add($("<input type='hidden' form='" + SAMPLE_FORM_NAME + "'/>"));
sampleFormAndHiddenInput.prependTo('body');
var sampleElementFound = sampleForm[0].elements[0];
sampleFormAndHiddenInput.remove();
if (sampleElementFound) {
// browser supports it, no need to fix
return;
}
/**
* Append a field to a form
*
*/
var CLASS_NAME_POLYFILL_MARKER = "html-5-polyfill-form-attr-marker";
$.fn.appendField = function(data) {
// for form only
if (!this.is('form')) return;
// wrap data
if (!$.isArray(data) && data.name && data.value) {
data = [data];
}
var $form = this;
// attach new params
$.each(data, function(i, item) {
$('<input/>')
.attr('type', 'hidden')
.attr('name', item.name)
.attr('class', CLASS_NAME_POLYFILL_MARKER)
.val(item.value).appendTo($form);
});
return $form;
};
/**
* Find all input fields with form attribute point to jQuery object
*
*/
$('form[id]').submit(function(e, origSubmit) {
// clean up form from last submit
$('.'+CLASS_NAME_POLYFILL_MARKER, this).remove();
// serialize data
var data = $('[form='+ this.id + ']').serializeArray();
// add data from external submit, if needed:
if (origSubmit && origSubmit.name)
data.push({name: origSubmit.name, value: origSubmit.value})
// append data to form
$(this).appendField(data);
})
//submit and reset behaviour
$('button[type=reset], input[type=reset]').click(function() {
//extend reset buttons to fields with matching form attribute
// reset form
var formId = $(this).attr("form");
var formJq = $('#'+formId);
if (formJq.length)
formJq[0].reset();
// for elements outside form
if (!formId)
formId = $(this).closest("form").attr("id");
$fields = $('[form=' + formId + ']');
$fields.each(function() {
this.value = this.defaultValue;
this.checked = this.defaultChecked;
}).filter('select').each(function() {
$(this).find('option').each(function() {
this.selected = this.defaultSelected;
});
});
});
$('button[type=submit], input[type=submit], input[type=image]').click(function() {
var formId = $(this).attr("form") || $(this).closest("form").attr("id");
$('#'+formId).trigger('submit', this); //send clicked submit as extra parameter
});
})(jQuery);
after reading thru the docs of webshim it seems it has a polyfill for that.
http://afarkas.github.io/webshim/demos/demos/webforms.html
I made a vanilla JavaScript polyfill based on the above polyfills and uploaded it on GitHub: https://github.com/Ununnilium/form-attribute-polyfill.
I also added a custom event to handle the case when submit is processed by JavaScript and not directly by the browser. I tested the code only shortly with IE 11, so please check it yourself before use. The polling should maybe be replaced by a more efficient detection function.
function browserNeedsPolyfill() {
var TEST_FORM_NAME = "form-attribute-polyfill-test";
var testForm = document.createElement("form");
testForm.setAttribute("id", TEST_FORM_NAME);
testForm.setAttribute("type", "hidden");
var testInput = document.createElement("input");
testInput.setAttribute("type", "hidden");
testInput.setAttribute("form", TEST_FORM_NAME);
testForm.appendChild(testInput);
document.body.appendChild(testInput);
document.body.appendChild(testForm);
var sampleElementFound = testForm.elements.length === 1;
document.body.removeChild(testInput);
document.body.removeChild(testForm);
return !sampleElementFound;
}
// Ideas from jQuery form attribute polyfill https://stackoverflow.com/a/26696165/2372674
function executeFormPolyfill() {
function appendDataToForm(data, form) {
Object.keys(data).forEach(function(name) {
var inputElem = document.createElement("input");
inputElem.setAttribute("type", "hidden");
inputElem.setAttribute("name", name);
inputElem.value = data[name];
form.appendChild(inputElem);
});
}
var forms = document.body.querySelectorAll("form[id]");
Array.prototype.forEach.call(forms, function (form) {
var fields = document.querySelectorAll('[form="' + form.id + '"]');
var dataFields = [];
Array.prototype.forEach.call(fields, function (field) {
if (field.disabled === false && field.hasAttribute("name")) {
dataFields.push(field);
}
});
Array.prototype.forEach.call(fields, function (field) {
if (field.type === "reset") {
field.addEventListener("click", function () {
form.reset();
Array.prototype.forEach.call(dataFields, function (dataField) {
if (dataField.nodeName === "SELECT") {
Array.prototype.forEach.call(dataField.querySelectorAll('option'), function (option) {
option.selected = option.defaultSelected;
});
} else {
dataField.value = dataField.defaultValue;
dataField.checked = dataField.defaultChecked;
}
});
});
} else if (field.type === "submit" || field.type === "image") {
field.addEventListener("click", function () {
var obj = {};
obj[field.name] = field.value;
appendDataToForm(obj, form);
form.dispatchEvent(eventToDispatch);
});
}
});
form.addEventListener("submit", function () {
var data = {};
Array.prototype.forEach.call(dataFields, function (dataField) {
data[dataField.name] = dataField.value;
});
appendDataToForm(data, form);
});
});
}
// Poll for new forms and execute polyfill for them
function detectedNewForms() {
var ALREADY_DETECTED_CLASS = 'form-already-detected';
var newForms = document.querySelectorAll('form:not([class="' + ALREADY_DETECTED_CLASS + '"])');
if (newForms.length !== 0) {
Array.prototype.forEach.call(newForms, function (form) {
form.className += ALREADY_DETECTED_CLASS;
});
executeFormPolyfill();
}
setTimeout(detectedNewForms, 100);
}
// Source: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
function polyfillCustomEvent() {
if (typeof window.CustomEvent === "function") {
return false;
}
function CustomEvent(event, params) {
params = params || {bubbles: false, cancelable: false, detail: undefined};
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
}
if (browserNeedsPolyfill()) {
polyfillCustomEvent(); // IE is missing CustomEvent
// This workaround is needed if submit is handled by JavaScript instead the browser itself
// Source: https://stackoverflow.com/a/35155789/2372674
var eventToDispatch = new CustomEvent("submit", {"bubbles": true, "cancelable": true});
detectedNewForms(); // Poll for new forms and execute form attribute polyfill for new forms
}
I take some time to send an update for this polyfill because it doesn't work with MS Edge.
I add 2 line to fix it :
var isEdge = navigator.userAgent.indexOf("Edge");
if (sampleElement && window.HTMLFormElement && sampleElement.form instanceof HTMLFormElement && !isIE11 && isEdge == -1) {
// browser supports it, no need to fix
return;
}
UPDATE: Edge now support it:
https://caniuse.com/#feat=form-attribute