Template:UFMG Brazil/live.js

From 2014.igem.org

/*
 Live.js - One script closer to Designing in the Browser
 Written for Handcraft.com by Martin Kool (@mrtnkl).
 Version 4.
 Recent change: Made stylesheet and mimetype checks case insensitive.
 http://livejs.com
 http://livejs.com/license (MIT)  
 @livejs
 Include live.js#css to monitor css changes only.
 Include live.js#js to monitor js changes only.
 Include live.js#html to monitor html changes only.
 Mix and match to monitor a preferred combination such as live.js#html,css  
 By default, just include live.js to monitor all css, js and html changes.
 
 Live.js can also be loaded as a bookmarklet. It is best to only use it for CSS then,
 as a page reload due to a change in html or css would not re-include the bookmarklet.
 To monitor CSS and be notified that it has loaded, include it as: live.js#css,notify
  • /

(function () {

 var headers = { "Etag": 1, "Last-Modified": 1, "Content-Length": 1, "Content-Type": 1 },
     resources = {},
     pendingRequests = {},
     currentLinkElements = {},
     oldLinkElements = {},
     interval = 1000,
     loaded = false,
     active = { "html": 1, "css": 1, "js": 1 };
 var Live = {
   // performs a cycle per interval
   heartbeat: function () {      
     if (document.body) {        
       // make sure all resources are loaded on first activation
       if (!loaded) Live.loadresources();
       Live.checkForChanges();
     }
     setTimeout(Live.heartbeat, interval);
   },
   // loads all local css and js resources upon first activation
   loadresources: function () {
     // helper method to assert if a given url is local
     function isLocal(url) {
       var loc = document.location,
           reg = new RegExp("^\\.|^\/(?!\/)|^[\\w]((?!://).)*$|" + loc.protocol + "//" + loc.host);
       return url.match(reg);
     }
     // gather all resources
     var scripts = document.getElementsByTagName("script"),
         links = document.getElementsByTagName("link"),
         uris = [];
     // track local js urls
     for (var i = 0; i < scripts.length; i++) {
       var script = scripts[i], src = script.getAttribute("src");
       if (src && isLocal(src))
         uris.push(src);
       if (src && src.match(/\blive.js#/)) {
         for (var type in active)
           active[type] = src.match("[#,|]" + type) != null
         if (src.match("notify")) 
           alert("Live.js is loaded.");
       }
     }
     if (!active.js) uris = [];
     if (active.html) uris.push(document.location.href);
     // track local css urls
     for (var i = 0; i < links.length && active.css; i++) {
       var link = links[i], rel = link.getAttribute("rel"), href = link.getAttribute("href", 2);
       if (href && rel && rel.match(new RegExp("stylesheet", "i")) && isLocal(href)) {
         uris.push(href);
         currentLinkElements[href] = link;
       }
     }
     // initialize the resources info
     for (var i = 0; i < uris.length; i++) {
       var url = uris[i];
       Live.getHead(url, function (url, info) {
         resources[url] = info;
       });
     }
     // add rule for morphing between old and new css files
     var head = document.getElementsByTagName("head")[0],
         style = document.createElement("style"),
         rule = "transition: all .3s ease-out;"
     css = [".livejs-loading * { ", rule, " -webkit-", rule, "-moz-", rule, "-o-", rule, "}"].join();
     style.setAttribute("type", "text/css");
     head.appendChild(style);
     style.styleSheet ? style.styleSheet.cssText = css : style.appendChild(document.createTextNode(css));
     // yep
     loaded = true;
   },
   // check all tracking resources for changes
   checkForChanges: function () {
     for (var url in resources) {
       if (pendingRequests[url])
         continue;
       Live.getHead(url, function (url, newInfo) {
         var oldInfo = resources[url],
             hasChanged = false;
         resources[url] = newInfo;
         for (var header in oldInfo) {
           // do verification based on the header type
           var oldValue = oldInfo[header],
               newValue = newInfo[header],
               contentType = newInfo["Content-Type"];
           switch (header.toLowerCase()) {
             case "etag":
               if (!newValue) break;
               // fall through to default
             default:
               hasChanged = oldValue != newValue;
               break;
           }
           // if changed, act
           if (hasChanged) {
             Live.refreshResource(url, contentType);
             break;
           }
         }
       });
     }
   },
   // act upon a changed url of certain content type
   refreshResource: function (url, type) {
     switch (type.toLowerCase()) {
       // css files can be reloaded dynamically by replacing the link element                               
       case "text/css":
         var link = currentLinkElements[url],
             html = document.body.parentNode,
             head = link.parentNode,
             next = link.nextSibling,
             newLink = document.createElement("link");
         html.className = html.className.replace(/\s*livejs\-loading/gi, ) + ' livejs-loading';
         newLink.setAttribute("type", "text/css");
         newLink.setAttribute("rel", "stylesheet");
         newLink.setAttribute("href", url + "?now=" + new Date() * 1);
         next ? head.insertBefore(newLink, next) : head.appendChild(newLink);
         currentLinkElements[url] = newLink;
         oldLinkElements[url] = link;
         // schedule removal of the old link
         Live.removeoldLinkElements();
         break;
       // check if an html resource is our current url, then reload                               
       case "text/html":
         if (url != document.location.href)
           return;
         // local javascript changes cause a reload as well
       case "text/javascript":
       case "application/javascript":
       case "application/x-javascript":
         document.location.reload();
     }
   },
   // removes the old stylesheet rules only once the new one has finished loading
   removeoldLinkElements: function () {
     var pending = 0;
     for (var url in oldLinkElements) {
       // if this sheet has any cssRules, delete the old link
       try {
         var link = currentLinkElements[url],
             oldLink = oldLinkElements[url],
             html = document.body.parentNode,
             sheet = link.sheet || link.styleSheet,
             rules = sheet.rules || sheet.cssRules;
         if (rules.length >= 0) {
           oldLink.parentNode.removeChild(oldLink);
           delete oldLinkElements[url];
           setTimeout(function () {
             html.className = html.className.replace(/\s*livejs\-loading/gi, );
           }, 100);
         }
       } catch (e) {
         pending++;
       }
       if (pending) setTimeout(Live.removeoldLinkElements, 50);
     }
   },
   // performs a HEAD request and passes the header info to the given callback
   getHead: function (url, callback) {
     pendingRequests[url] = true;
     var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XmlHttp");
     xhr.open("HEAD", url, true);
     xhr.onreadystatechange = function () {
       delete pendingRequests[url];
       if (xhr.readyState == 4 && xhr.status != 304) {
         xhr.getAllResponseHeaders();
         var info = {};
         for (var h in headers) {
           var value = xhr.getResponseHeader(h);
           // adjust the simple Etag variant to match on its significant part
           if (h.toLowerCase() == "etag" && value) value = value.replace(/^W\//, );
           if (h.toLowerCase() == "content-type" && value) value = value.replace(/^(.*?);.*?$/i, "$1");
           info[h] = value;
         }
         callback(url, info);
       }
     }
     xhr.send();
   }
 };
 // start listening
 if (document.location.protocol != "file:") {
   if (!window.liveJsLoaded)
     Live.heartbeat();
   window.liveJsLoaded = true;
 }
 else if (window.console)
   console.log("Live.js doesn't support the file protocol. It needs http.");    

})();