Single threaded UI
Useful for heavy JS loads, such as...
Access to:
but not the DOM or caller
Create a worker
var workerName = new Worker('javascriptFile.js');
Communicate with worker
workerName.postMessage(message_for_worker);
Listen to worker
self.onmessage = function (event) { var theMessage = event.data; }
Error Handling
self.onerror = function(error){ var caller = error.filename; var errorLineNumber = error.lineno; var errorMsg = error.message; }
In this lab, we are going to create a worker that calculate a fibonacci number sequence and display the result to the page that has spawned the worker
Currently, the fibonacci sequence is calculated in the same thread as the UI thread. For small sequences, it works fine but if you ask for bigger sequences, it starts to become problematic and the browser thinks that the script is unresponsive
In your labs folder open index.html in a browser. The button to calculate with web worker is not implemented yet. Try to calculate a fibonacci sequence of 10. Then try with 100
Note: Because of Chrome security restrictions, you cannot run your app from the file:// scheme unless you start Chrome with the --allow-file-access-from-files flag
URL | Same Origin | Issue |
---|---|---|
http://www.a.com/ | Yes | |
http://www.a.com/folder/page.php | Yes | |
https://www.a.com/ | No | Different Protocol |
http://blog.a.com | No | Different Host |
http://www.a.com:1234 | No | Different Port |
OLD: Solutions: JSON, Proxy, PHP CURL, iFrames
New Solutions: CORS, Cross-document Messaging
Cross-Origin Resource Sharing
Method of performing XMLHttpRequests across domains
Need correct headers sent and received
Allows responses to permitted origin domains (or * if public)
IE8 - XDomainRequest Object
IE10 & modern browsers: XMLHttpRequest
Request Headers
Origin: http://requestingdomain.com Access-Control-Request-Headers: PASSPHRASE Access-Control-Request-Method:POST
Response Headers
Access-Control-Allow-Origin: * # or domain Access-Control-Allow-Methods:POST, GET, OPTIONS Access-Control-Allow-Headers: PASSPHRASE Access-Control-Max-Age: 86400
CORS feature detection withCredentials
Property
if (XMLHttpRequest) { var request = new XMLHttpRequest(); var url = http://otherserver.com/resource; // FEATURE DETECTION if (request.withCredentials !== undefined) { request.open('GET', url, true); request.onreadystatechange = handleEvent; request.send(); } }
Similar, but sends http cookie header with request (not the default)
if (XMLHttpRequest) { var request = new XMLHttpRequest(); var url = http://otherserver.com/resource; // FEATURE DETECTION if (request.withCredentials !== undefined) { request.open('GET', url, true); request.withCredentials = "true"; request.onreadystatechange = handler; request.send(); } }
get "preapproval" for request
OPTIONS
header from requestor Code snippet →
var request = new XMLHttpRequest(); var url = 'http://otherdomain.com/file'; var requestHistoryText; var body = 'some text'; function callOtherDomain(){ if(request) { request.open('POST', url, true); request.setRequestHeader('X-MADEUP', 'pingpong'); request.setRequestHeader('Content-Type', 'application/xml'); request.onreadystatechange = handleEvent; request.send(body); } else { console.log("Request Failed"); } } function handleEvent(evtXHR) { if (request.readyState == 4) { if (request.status == 200) { var response = request.responseText; requestHistoryText = document.createTextNode(response); var textDiv = document.getElementById("textDiv"); textDiv.appendChild(requestHistoryText); } else { console.log("request Errors Occured " + request.readyState + " and the status is " + request.status); } } else { dump("currently the application is at" + request.readyState); } }
otherWindow.postMessage(message, targetOrigin);
otherWindow needs to listen for message
window.addEventListener("message", handleMessage, false); function handleMessage(event) { if (event.origin !== "http://messageOriginator.com") return; // safe ... handle message var receivedMsg = event.data; var receivedFromWindow = event.source; }
Also, dont' include message listener if none expected
And, always verify the syntax of the received message.
Real-time, full duplex communication using a single socket
Half duplex solutions that are immediately old, and require http requests (including sending headers (with) cookies back and forth)
GET /chat HTTP/1.1 Connection: Upgrade Host: example.com Origin: http://example.com Sec-WebSocket-Key1: 284 ^rI 2 447 8 Me1*V 8* Sec-WebSocket-Key2: 30]8N763$84 12>* Upgrade: WebSocket 64:6E:AC:0C:FD:90:8A:51* *(Random tokens for 16 byte token)
HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Origin: http://example.com
Sec-WebSocket-Location: ws://example.com/chat
79:C5:C1:29:4A:60:8B:34:66:D5:61:10:C2:0C:4F:AA *The token
Request headers from the client plus the 8 byte token to say that the client handshake has been read. Once handshake established, the server can send messages to client
// ready state CONNECTING = 0 OPEN = 1 CLOSED = 2 readyState bufferedAmount // networking onopen onmessage onclose send(data) close()
Create Socket
var socket=new WebSocket("ws://127.0.0.1:8080/")
Event Listeners
socket.onopen = function(){ console.log("Connection to the socket is now open"); } socket.onmessage = function(e){ console.log("Message received: " + e.data); } socket.onclose = function(e){ console.log("Connection to the socket is closed"); }
Send Message
socket.send("This is the message sent by the client")
Load data & change browser address bar (without reloading)
window.history.pushState(data, title, url) /* Pushes data into history, with title, and, if provided, the URL.*/
window.history.replaceState(data, title, url) /* Updates current history entry to given data, with title, and, if provided, URL */
window.history.length /* number of entries in session history */ window.history.state /* current state */ window.history.go( Δ ) /* back or forward Δ steps */ window.history.back() /* Goes back one step Δ== -1*/ window.history.forward() /* Goes forward one step Δ = 1*/
document.addEventListener('dragstart', function(event) { event.dataTransfer.setData('text', 'Customized text'); event.dataTransfer.effectAllowed = 'copy'; }, false);
Drag this text or the image above to the right. You can even change this text.
Drop Area
var goFullScreen = function(){ var doc = document.documentElement; if (doc.requestFullscreen) { doc.requestFullscreen(); } else if (docElm.mozRequestFullScreen) { doc.mozRequestFullScreen(); } else if (docElm.webkitRequestFullScreen) { doc.webkitRequestFullScreen(); } } var exitFullScreen = function(){ if (document.exitFullscreen) { document.exitFullscreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.webkitCancelFullScreen) { document.webkitCancelFullScreen(); } }
var getFullScreenState = function(){ if(document.exitFullscreen) { return (document.fullscreen) ? true : false; } if (document.mozCancelFullScreen) { return (document.mozFullScreen) ? true : false; } if (document.webkitCancelFullScreen) { return (document.webkitIsFullScreen) ? true : false; } }
document.addEventListener("fullscreenchange"... document.addEventListener("mozfullscreenchange"... document.addEventListener("webkitfullscreenchange"...
:fullscreen & :fullscreen-ancestor
Tutorial: Talk from Google I/O
<input type="file" id="files" accept="image/*" multiple> document.querySelector('#files').onchange = function(e) { var files = e.target.files; // FileList of File objects. for (var i = 0, f; f = files[i]; ++i) { console.log(f.name, f.type, f.size, f.lastModifiedDate.toLocaleDateString()); } };
var elements = document.getElementsByClassName(className)
var element = document.querySelector(selectors); var element = $(selectors)[0]; var elements = document.querySelectorAll(selectors); var elements = $(selectors);
Where 'selectors' is a comma separated list of CSS selectors
<article id="main" class="blogpost popup"></article>
var el = document.getElementById('main').classList; var el = document.querySelector('#main').classList; el.add('bgimage');
el.remove('blogpost'); el.toggle('popup'); console.log(el.contains('popup')); // false console.log(el.contains('blogpost')); // false console.log(el.classList.toString() == el.className); // true
Result:
<article id="main" class="bgimage"></article>
Determine if your media is visible or not:
document.addEventListener('visibilitychange', function(e) { console.log('hidden:' + document.hidden, 'state:' + document.visibilityState) }, false);
// set name of hidden property and visibility change event var hidden, visibilityChange; if (typeof document.hidden !== "undefined") { hidden = "hidden"; visibilityChange = "visibilitychange"; } else if (typeof document.mozHidden !== "undefined") { hidden = "mozHidden"; visibilityChange = "mozvisibilitychange"; } else if (typeof document.msHidden !== "undefined") { hidden = "msHidden"; visibilityChange = "msvisibilitychange"; } else if (typeof document.webkitHidden !== "undefined") { hidden = "webkitHidden"; visibilityChange = "webkitvisibilitychange"; } var videoElement = document.getElementById("videoElement"); // if the page is hidden, pause the video // if the page is shown, play the video function handleVisibilityChange() { if (document[hidden]) { videoElement.pause(); } else { videoElement.play(); } } // warn if the browser doesn't support addEventListener or the Page Visibility API if (typeof document.addEventListener === "undefined" || typeof hidden === "undefined") { alert("This demo requires a browser such as Google Chrome that supports the Page Visibility API."); } else { // handle page visibility change // see https://developer.mozilla.org/en/API/PageVisibility/Page_Visibility_API document.addEventListener(visibilityChange, handleVisibilityChange, false); // revert to existing favicon for site when the page is closed // otherwise the favicon will remain as paused.png window.addEventListener("unload", function(){ favicon.change("/favicon.ico"); }, false); // when the video pauses, set the favicon videoElement.addEventListener("pause", function(){ favicon.change("images/paused.png"); }, false); // when the video plays, set the favicon videoElement.addEventListener("play", function(){ favicon.change("images/playing.png"); }, false); // set the document (tab) title from the current video time videoElement.addEventListener("timeupdate", function(){ document.title = Math.floor(videoElement.currentTime) + " second(s)"; }, false);
navigator.getUserMedia({audio: true, video: true}, function(s) { var video = document.querySelector('video'); video.src = window.URL.createObjectURL(s); }, function(e) { console.log(e); });
if (window.webkitNotifications.checkPermission() == 0) { window.webkitNotifications.createNotification(picture, title, content).show(); } else { window.webkitNotifications.requestPermission(); }