From f4f802e63d31c9c560d9b486afb9d8d128997f1e Mon Sep 17 00:00:00 2001 From: Matias Carranza Date: Thu, 16 Oct 2014 13:53:16 -0300 Subject: [PATCH 1/9] format and heatmap changes using js format change the way heatmap is built --- perfmap.js | 228 ++++++++++++++++++++++++++++------------------------- 1 file changed, 122 insertions(+), 106 deletions(-) diff --git a/perfmap.js b/perfmap.js index 5dbb72e..da31efc 100644 --- a/perfmap.js +++ b/perfmap.js @@ -1,11 +1,30 @@ var gZeroLeft = 0; var gZeroTop = 0; var gWinWidth = window.innerWidth || document.documentElement.clientWidth; +var hArr = [{ + threashold: 0.16, + value: "#1a9850" +}, { + threashold: 0.32, + value: "#66bd63" +}, { + threashold: 0.48, + value: "#a6d964" +}, { + threashold: 0.64, + value: "#fdae61" +}, { + threashold: 0.8, + value: "#f46d43" +}, { + threashold: 1.1, + value: "#d73027" +}]; function findImages() { var aElems = document.getElementsByTagName('*'); var re = /url\((http.*)\)/ig; - for ( var i=0, len = aElems.length; i < len; i++ ) { + for (var i = 0, len = aElems.length; i < len; i++) { var elem = aElems[i]; var style = window.getComputedStyle(elem); var url = elem.src || elem.href; @@ -14,105 +33,102 @@ function findImages() { var body = 0; re.lastIndex = 0; // reset state of regex so we catch repeating spritesheet elements if (elem.tagName == 'IMG') { - hasImage = 1; - } - if (style['background-image']) { - var backgroundImage = style['background-image']; - var matches = re.exec(style['background-image']); - if (matches && matches.length > 1){ - url = backgroundImage.substring(4); - url = url.substring(0, url.length - 1); - hasImage = 1; - if(elem.tagName == 'BODY'){ - body = 1; - } - } - } - if (style['visibility'] == "hidden") { - hasImage = 0; - } - if(hasImage == 1){ - if ( url ) { - var entry = performance.getEntriesByName(url)[0]; - if ( entry ) { - var xy = getCumulativeOffset(elem, url); - var wh = elem.getBoundingClientRect(); - var width = wh.width; - var height = wh.height; - if(width > 10){ - if(height > 10){ - placeMarker(xy, width, height, entry, body, url); - } - } - } - } - } + hasImage = 1; + } + if (style['background-image']) { + var backgroundImage = style['background-image']; + var matches = re.exec(style['background-image']); + if (matches && matches.length > 1) { + url = backgroundImage.substring(4); + url = url.substring(0, url.length - 1); + hasImage = 1; + if (elem.tagName == 'BODY') { + body = 1; + } + } + } + if (style['visibility'] == "hidden") { + hasImage = 0; + } + if (hasImage == 1) { + if (url) { + var entry = performance.getEntriesByName(url)[0]; + if (entry) { + var xy = getCumulativeOffset(elem, url); + var wh = elem.getBoundingClientRect(); + var width = wh.width; + var height = wh.height; + if (width > 10) { + if (height > 10) { + placeMarker(xy, width, height, entry, body, url); + } + } + } + } + } } } function placeMarker(xy, width, height, entry, body, url) { - var heat = entry.responseEnd / loaded; - // adjust size of fonts/padding based on width of overlay - if(width < 170){ - var padding = 12; - var size = 12; - }else if(width > 400){ - var padding = 13; - var size = 26; - }else{ - var padding = 9; - var size = 18; - } - // check for overlay that matches viewport and assume it's like a background image on body - if(width == document.documentElement.clientWidth){ - if(height >= document.documentElement.clientHeight){ - body = 1; - } - } - // adjust opacity if it's the body element and position label top right - if(body == 1){ - var opacity = 0.6; - var size = 18; - var align = "right"; - var paddingTop = 10; - var bodyText = "BODY "; - }else{ - var opacity = 0.925; - var align = "center"; - var paddingTop = (height/2)-padding; - var bodyText = ""; - } + var heat = entry.responseEnd / loaded; + // adjust size of fonts/padding based on width of overlay + if (width < 170) { + var padding = 12; + var size = 12; + } else if (width > 400) { + var padding = 13; + var size = 26; + } else { + var padding = 9; + var size = 18; + } + // check for overlay that matches viewport and assume it's like a background image on body + if (width == document.documentElement.clientWidth) { + if (height >= document.documentElement.clientHeight) { + body = 1; + } + } + // adjust opacity if it's the body element and position label top right + if (body == 1) { + var opacity = 0.6; + var size = 18; + var align = "right"; + var paddingTop = 10; + var bodyText = "BODY "; + } else { + var opacity = 0.925; + var align = "center"; + var paddingTop = (height / 2) - padding; + var bodyText = ""; + } var marker = document.createElement("div"); marker.className = "perfmap"; marker.setAttribute("data-ms", parseInt(entry.responseEnd)); marker.setAttribute("data-body", body); - marker.style.cssText = "position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: " + size + "px; font-weight:800; font-family:\"Helvetica Neue\",sans-serif; text-align:" + align + "; opacity: " + opacity + "; " + heatmap(heat) + " top: " + xy.top + "px; left: " + xy.left + "px; width: " + width + "px; height:" + height + "px; padding-top:" + paddingTop + "px; z-index: 4000;"; - if(width > 50){ - if(height > 15 ){ - marker.innerHTML = bodyText + parseInt(entry.responseEnd) + "ms (" + parseInt(entry.duration) + "ms)"; - } + marker.style.cssText = "position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: " + size + "px; font-weight:800; font-family:\"Helvetica Neue\",sans-serif; text-align:" + align + "; opacity: " + opacity + "; background: " + heatmap(heat) + " top: " + xy.top + "px; left: " + xy.left + "px; width: " + width + "px; height:" + height + "px; padding-top:" + paddingTop + "px; z-index: 4000;"; + if (width > 50) { + if (height > 15) { + marker.innerHTML = bodyText + parseInt(entry.responseEnd) + "ms (" + parseInt(entry.duration) + "ms)"; + } } document.body.appendChild(marker); } function heatmap(heat) { - if ( heat < 0.16 ) { - return "background: #1a9850;" - } - else if ( heat < 0.32 ) { - return "background: #66bd63;" - } - else if ( heat < 0.48 ) { - return "background: #a6d96a;" - } - else if ( heat < 0.64 ) { - return "background: #fdae61;" - } - else if ( heat < 0.8 ) { - return "background: #f46d43;" - }else{ - return "background: #d73027;" + function findIndex(array, heat, predicate) { + var index = -1, + length = array ? array.length : 0; + while (++index < length) { + if (predicate(array[index], index, array)) { + return array[index]; + } + } + return array[length - 1]; } + + return findIndex(hArr, function(chr) { + return heat < chr.threashold; + }); } function getCumulativeOffset(obj, url) { @@ -121,10 +137,10 @@ function getCumulativeOffset(obj, url) { if (obj.offsetParent) { do { left += obj.offsetLeft; - top += obj.offsetTop; + top += obj.offsetTop; } while (obj = obj.offsetParent); } - if ( 0 == top ) { + if (0 == top) { left += gZeroLeft; top += gZeroTop; } @@ -146,18 +162,18 @@ var loaded = performance.timing.loadEventEnd - performance.timing.navigationStar // backend var backend = performance.timing.responseEnd - performance.timing.navigationStart; -var backendLeft = (backend / loaded)*100; +var backendLeft = (backend / loaded) * 100; // first paint in chrome from https://github.com/addyosmani/timing.js if (window.chrome && window.chrome.loadTimes) { - var paint = window.chrome.loadTimes().firstPaintTime * 1000; - var firstPaint = paint - (window.chrome.loadTimes().startLoadTime*1000); - var firstPaintLeft = (firstPaint / loaded)*100; + var paint = window.chrome.loadTimes().firstPaintTime * 1000; + var firstPaint = paint - (window.chrome.loadTimes().startLoadTime * 1000); + var firstPaintLeft = (firstPaint / loaded) * 100; } // remove any exisiting "perfmap" divs on second click var elements = document.getElementsByClassName("perfmap"); -while(elements.length > 0){ +while (elements.length > 0) { elements[0].parentNode.removeChild(elements[0]); } @@ -177,19 +193,19 @@ loading.remove(); // mouse events to move timeline around on hover var elements = document.getElementsByClassName("perfmap"); var timeline = document.getElementById('perfmap-timeline'); -for ( var i=0, len = elements.length; i < len; i++ ) { - elements[i].onmouseover = function(){ - var timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); - if(this.dataset.body != "1"){ - this.style.opacity = 1; - } - timeline.style.cssText = "opacity:1; transition: 0.5s ease-in-out; transform: translate("+ parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; +for (var i = 0, len = elements.length; i < len; i++) { + elements[i].onmouseover = function() { + var timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); + if (this.dataset.body != "1") { + this.style.opacity = 1; + } + timeline.style.cssText = "opacity:1; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; } - elements[i].onmouseout = function(){ - var timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); - if(this.dataset.body != "1"){ - this.style.opacity = 0.925; - } - timeline.style.cssText = "opacity:0; transition: 0.5s ease-in-out; transform: translate("+ parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + elements[i].onmouseout = function() { + var timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); + if (this.dataset.body != "1") { + this.style.opacity = 0.925; + } + timeline.style.cssText = "opacity:0; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; } } \ No newline at end of file From 41177067da6012672db12cee461f602953a8fd2c Mon Sep 17 00:00:00 2001 From: Matias Carranza Date: Thu, 16 Oct 2014 20:19:38 -0300 Subject: [PATCH 2/9] IIF and refactoring IIF avoid polluting global scope several jshint fixes refactor --- perfmap.js | 432 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 241 insertions(+), 191 deletions(-) diff --git a/perfmap.js b/perfmap.js index da31efc..c797e49 100644 --- a/perfmap.js +++ b/perfmap.js @@ -1,211 +1,261 @@ -var gZeroLeft = 0; -var gZeroTop = 0; -var gWinWidth = window.innerWidth || document.documentElement.clientWidth; -var hArr = [{ - threashold: 0.16, - value: "#1a9850" -}, { - threashold: 0.32, - value: "#66bd63" -}, { - threashold: 0.48, - value: "#a6d964" -}, { - threashold: 0.64, - value: "#fdae61" -}, { - threashold: 0.8, - value: "#f46d43" -}, { - threashold: 1.1, - value: "#d73027" -}]; - -function findImages() { - var aElems = document.getElementsByTagName('*'); - var re = /url\((http.*)\)/ig; - for (var i = 0, len = aElems.length; i < len; i++) { - var elem = aElems[i]; - var style = window.getComputedStyle(elem); - var url = elem.src || elem.href; - var hasImage = 0; - var fixed = 0; - var body = 0; - re.lastIndex = 0; // reset state of regex so we catch repeating spritesheet elements - if (elem.tagName == 'IMG') { - hasImage = 1; +(function(window, document, undefined) { + 'use strict'; + var perfMap = {}; + + + var loading, + loaded, + gZeroLeft = 0, + gZeroTop = 0, + hArr = [{ + threashold: 0.16, + value: "#1a9850" + }, { + threashold: 0.32, + value: "#66bd63" + }, { + threashold: 0.48, + value: "#a6d964" + }, { + threashold: 0.64, + value: "#fdae61" + }, { + threashold: 0.8, + value: "#f46d43" + }, { + threashold: 1.1, + value: "#d73027" + }]; + + function findImages() { + var tags = document.getElementsByTagName('*'), + images = document.getElementsByTagName('img'), + el, + imgs = []; + //re = /url\(([http].*)\)/ig; + //re = /(url)\((.*?)\)/ig; + + function getBgElement(el) { + if (!!el.currentStyle) { + if (el.currentStyle['backgroundImage'] !== 'none') { + //el.className += ' bg_found'; + return { + element: el, + src: el.currentStyle['backgroundImage'] + }; + } + } else if (!!window.getComputedStyle) { + if (document.defaultView.getComputedStyle(el, null).getPropertyValue('background-image') !== 'none') { + //el.className += ' bg_found'; + return { + element: el, + src: document.defaultView.getComputedStyle(el, null).getPropertyValue('background-image') + }; + } + } } - if (style['background-image']) { - var backgroundImage = style['background-image']; - var matches = re.exec(style['background-image']); - if (matches && matches.length > 1) { - url = backgroundImage.substring(4); - url = url.substring(0, url.length - 1); - hasImage = 1; - if (elem.tagName == 'BODY') { - body = 1; + + for (var i = 0, len = tags.length; i < len; i++) { + //imgs.push(getBgElement(tags[i])); + var el = getBgElement(tags[i]); + if (!!el && !!el.src) { + var match = el.src.match(/\((.*?)\)/); + if (match[1]) { + el.src = match[1].replace(/('|")/g, ''); + /*if (style['visibility'] == "hidden") { + hasImage = 0; + } else { + hasImage = 1; + if (elem.tagName == 'BODY') { + body = 1; + } + imgs.push(el); + }*/ + imgs.push(el); } } } - if (style['visibility'] == "hidden") { - hasImage = 0; + + for (var i = 0; i < images.length; i++) { + imgs.push({ + element: images[i], + src: images[i].src + }); } - if (hasImage == 1) { - if (url) { - var entry = performance.getEntriesByName(url)[0]; - if (entry) { - var xy = getCumulativeOffset(elem, url); - var wh = elem.getBoundingClientRect(); - var width = wh.width; - var height = wh.height; - if (width > 10) { - if (height > 10) { - placeMarker(xy, width, height, entry, body, url); - } + + //console.log(imgs); + for (var i = 0, len = imgs.length; i < len; i++) { + var entry = window.performance.getEntriesByName(imgs[i].src)[0]; + if (entry) { + var xy = getCumulativeOffset(imgs[i].element, imgs[i].src); + var wh = imgs[i].element.getBoundingClientRect(); + var width = wh.width; + var height = wh.height; + if (width > 10) { + if (height > 10) { + placeMarker(xy, width, height, entry, imgs[i].element.tagName === 'BODY', imgs[i].src); } } } } } -} - -function placeMarker(xy, width, height, entry, body, url) { - var heat = entry.responseEnd / loaded; - // adjust size of fonts/padding based on width of overlay - if (width < 170) { - var padding = 12; - var size = 12; - } else if (width > 400) { - var padding = 13; - var size = 26; - } else { - var padding = 9; - var size = 18; - } - // check for overlay that matches viewport and assume it's like a background image on body - if (width == document.documentElement.clientWidth) { - if (height >= document.documentElement.clientHeight) { - body = 1; + + + function placeMarker(xy, width, height, entry, body, url) { + var heat = (entry.responseEnd / loaded), + marker = document.createElement("div"), + padding = 9, + size = 18, + paddingTop, + opacity = 0.925, + align = "center", + bodyText = ""; + + // adjust size of fonts/padding based on width of overlay + if (width < 170) { + padding = 12; + size = 12; + } else if (width > 400) { + padding = 13; + size = 26; } - } - // adjust opacity if it's the body element and position label top right - if (body == 1) { - var opacity = 0.6; - var size = 18; - var align = "right"; - var paddingTop = 10; - var bodyText = "BODY "; - } else { - var opacity = 0.925; - var align = "center"; - var paddingTop = (height / 2) - padding; - var bodyText = ""; - } - var marker = document.createElement("div"); - marker.className = "perfmap"; - marker.setAttribute("data-ms", parseInt(entry.responseEnd)); - marker.setAttribute("data-body", body); - marker.style.cssText = "position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: " + size + "px; font-weight:800; font-family:\"Helvetica Neue\",sans-serif; text-align:" + align + "; opacity: " + opacity + "; background: " + heatmap(heat) + " top: " + xy.top + "px; left: " + xy.left + "px; width: " + width + "px; height:" + height + "px; padding-top:" + paddingTop + "px; z-index: 4000;"; - if (width > 50) { - if (height > 15) { - marker.innerHTML = bodyText + parseInt(entry.responseEnd) + "ms (" + parseInt(entry.duration) + "ms)"; + + // check for overlay that matches viewport and assume it's like a background image on body + if ((width === document.documentElement.clientWidth) && (height >= document.documentElement.clientHeight)) { + body = true; } - } - document.body.appendChild(marker); -} - -function heatmap(heat) { - function findIndex(array, heat, predicate) { - var index = -1, - length = array ? array.length : 0; - while (++index < length) { - if (predicate(array[index], index, array)) { - return array[index]; + + // adjust opacity if it's the body element and position label top right + paddingTop = (height / 2) - padding; + if (!!body) { + opacity = 0.6; + size = 18; + align = "right"; + paddingTop = 10; + bodyText = "BODY "; + } + + marker.className = "perfmap"; + marker.setAttribute("data-ms", parseInt(entry.responseEnd)); + marker.setAttribute("data-body", (body ? 1 : 0)); + marker.style.cssText = "position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: " + size + "px; font-weight:800; font-family:\"Helvetica Neue\",sans-serif; text-align:" + align + "; opacity: " + opacity + "; background: " + heatmap(heat).value + "; top: " + xy.top + "px; left: " + xy.left + "px; width: " + width + "px; height:" + height + "px; padding-top:" + paddingTop + "px; z-index: 4000;"; + if (width > 50) { + if (height > 15) { + marker.innerHTML = bodyText + parseInt(entry.responseEnd) + "ms (" + parseInt(entry.duration) + "ms)"; } } - return array[length - 1]; - } - - return findIndex(hArr, function(chr) { - return heat < chr.threashold; - }); -} - -function getCumulativeOffset(obj, url) { - var left, top; - left = top = 0; - if (obj.offsetParent) { - do { - left += obj.offsetLeft; - top += obj.offsetTop; - } while (obj = obj.offsetParent); + document.body.appendChild(marker); } - if (0 == top) { - left += gZeroLeft; - top += gZeroTop; + + function heatmap(heat) { + function findIndex(array, predicate) { + var index = -1, + length = array ? array.length : 0; + while (++index < length) { + if (predicate(array[index], index, array)) { + return array[index]; + } + } + return array[length - 1]; + } + + return findIndex(hArr, function(chr) { + return heat < chr.threashold; + }); } - return { - left: left, - top: top, - }; -} - -// give visual feedback asap -var loading = document.createElement("div"); -loading.id = "perfmap-loading"; -loading.innerHTML = "Creating PerfMap"; -loading.style.cssText = "position:absolute; z-index:6000; left:40%; top:45%; background-color:#000; color:#fff; padding:20px 30px; font-family:\"Helvetica Neue\",sans-serif; font-size:24px; font-weight:800;border:2px solid white;"; -document.body.appendChild(loading); - -// get full page load time to calculate heatmap max -var loaded = performance.timing.loadEventEnd - performance.timing.navigationStart; - -// backend -var backend = performance.timing.responseEnd - performance.timing.navigationStart; -var backendLeft = (backend / loaded) * 100; - -// first paint in chrome from https://github.com/addyosmani/timing.js -if (window.chrome && window.chrome.loadTimes) { - var paint = window.chrome.loadTimes().firstPaintTime * 1000; - var firstPaint = paint - (window.chrome.loadTimes().startLoadTime * 1000); - var firstPaintLeft = (firstPaint / loaded) * 100; -} - -// remove any exisiting "perfmap" divs on second click -var elements = document.getElementsByClassName("perfmap"); -while (elements.length > 0) { - elements[0].parentNode.removeChild(elements[0]); -} - -// build bottom legend -var perfmap = document.createElement("div"); -perfmap.id = "perfmap"; -perfmap.style.cssText = "position: fixed; width:100%; bottom:0; left:0; z-index:5000; height: 25px; color:#fff; font-family:\"Helvetica Neue\",sans-serif; font-size:14px; font-weight:800; line-height:14px;"; -perfmap.innerHTML = "
Fully Loaded " + parseInt(loaded) + "ms
First Paint " + parseInt(firstPaint) + "ms
"; -document.body.appendChild(perfmap); - -// build heatmap -findImages(); - -// remove loading message -loading.remove(); - -// mouse events to move timeline around on hover -var elements = document.getElementsByClassName("perfmap"); -var timeline = document.getElementById('perfmap-timeline'); -for (var i = 0, len = elements.length; i < len; i++) { - elements[i].onmouseover = function() { - var timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); - if (this.dataset.body != "1") { - this.style.opacity = 1; + + function getCumulativeOffset(obj, url) { + var left, top; + left = top = 0; + if (obj.offsetParent) { + do { + left += obj.offsetLeft; + top += obj.offsetTop; + } while (obj = obj.offsetParent); } - timeline.style.cssText = "opacity:1; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + if (0 === top) { + left += gZeroLeft; + top += gZeroTop; + } + return { + left: left, + top: top, + }; } - elements[i].onmouseout = function() { - var timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); - if (this.dataset.body != "1") { - this.style.opacity = 0.925; + + function startLoading() { + + // give visual feedback asap + loading = document.createElement("div"); + loading.id = "perfmap-loading"; + loading.innerHTML = "Creating PerfMap"; + loading.style.cssText = "position:absolute; z-index:6000; left:40%; top:45%; background-color:#000; color:#fff; padding:20px 30px; font-family:\"Helvetica Neue\",sans-serif; font-size:24px; font-weight:800;border:2px solid white;"; + document.body.appendChild(loading); + + // get full page load time to calculate heatmap max + loaded = window.performance.timing.loadEventEnd - window.performance.timing.navigationStart; + + // backend + var backend = window.performance.timing.responseEnd - window.performance.timing.navigationStart; + var backendLeft = (backend / loaded) * 100; + var paint, firstPaint, firstPaintLeft; + + // first paint in chrome from https://github.com/addyosmani/timing.js + if (window.chrome && window.chrome.loadTimes) { + paint = window.chrome.loadTimes().firstPaintTime * 1000; + firstPaint = paint - (window.chrome.loadTimes().startLoadTime * 1000); + firstPaintLeft = (firstPaint / loaded) * 100; } - timeline.style.cssText = "opacity:0; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + + // remove any exisiting "perfmap" divs on second click + var elements = document.getElementsByClassName("perfmap"); + while (elements.length > 0) { + elements[0].parentNode.removeChild(elements[0]); + } + + // build bottom legend + var perfmap = document.createElement("div"); + perfmap.id = "perfmap"; + perfmap.style.cssText = "position: fixed; width:100%; bottom:0; left:0; z-index:5000; height: 25px; color:#fff; font-family:\"Helvetica Neue\",sans-serif; font-size:14px; font-weight:800; line-height:14px;"; + perfmap.innerHTML = "
Fully Loaded " + parseInt(loaded) + "ms
First Paint " + parseInt(firstPaint) + "ms
"; + document.body.appendChild(perfmap); + + } -} \ No newline at end of file + + + + perfMap.init = function() { + startLoading(); + // build heatmap + findImages(); + + // remove loading message + loading.remove(); + + // mouse events to move timeline around on hover + var elements = document.getElementsByClassName("perfmap"); + var timeline = document.getElementById('perfmap-timeline'); + var timelineLeft; + for (var i = 0, len = elements.length; i < len; i++) { + elements[i].onmouseover = function() { + timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); + if (this.dataset.body != "1") { + this.style.opacity = 1; + } + timeline.style.cssText = "opacity:1; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + }; + elements[i].onmouseout = function() { + timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); + if (this.dataset.body != "1") { + this.style.opacity = 0.925; + } + timeline.style.cssText = "opacity:0; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + }; + } + }; + + window.perfMap = perfMap; +})(window, document); + +perfMap.init(); \ No newline at end of file From 8d738abd473ff67a8e4542853b1dce6b5aff7296 Mon Sep 17 00:00:00 2001 From: Matias Carranza Date: Sun, 19 Oct 2014 23:12:16 -0300 Subject: [PATCH 3/9] jasmine, jshit, grunt, split in little files --- .gitignore | 3 + .jshintrc | 21 +++ Gruntfile.js | 59 +++++++++ build/perfmap.js | 256 +++++++++++++++++++++++++++++++++++++ package.json | 13 ++ specs/getBgImages.spec.js | 80 ++++++++++++ specs/test.spec.js | 53 ++++++++ src/_vars.js | 24 ++++ src/findImages.js | 50 ++++++++ src/getBgImages.js | 14 ++ src/getCumulativeOffset.js | 20 +++ src/getTagImages.js | 15 +++ src/heatmap.js | 18 +++ src/init.js | 31 +++++ src/placeMarker.js | 47 +++++++ src/startLoading.js | 41 ++++++ 16 files changed, 745 insertions(+) create mode 100644 .gitignore create mode 100644 .jshintrc create mode 100644 Gruntfile.js create mode 100644 build/perfmap.js create mode 100644 package.json create mode 100644 specs/getBgImages.spec.js create mode 100644 specs/test.spec.js create mode 100644 src/_vars.js create mode 100644 src/findImages.js create mode 100644 src/getBgImages.js create mode 100644 src/getCumulativeOffset.js create mode 100644 src/getTagImages.js create mode 100644 src/heatmap.js create mode 100644 src/init.js create mode 100644 src/placeMarker.js create mode 100644 src/startLoading.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fb88a33 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.grunt + diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..0c3989c --- /dev/null +++ b/.jshintrc @@ -0,0 +1,21 @@ +{ + "node": true, + "browser": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 4, + "latedef": false, + "newcap": true, + "noarg": true, + "quotmark": "single", + "regexp": true, + "smarttabs": true, + "strict": true, + "trailing": true, + "undef": true, + "validthis": true +} \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..d18a05e --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,59 @@ +module.exports = function(grunt) { + + // Project configuration. + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + wrap: { + basic: { + src: ['build/perfmap.js'], + dest: 'build/perfmap.js', + options: { + wrapper: ['\n;(function(window, document, undefined){\n\'use strict\';\n', '\nwindow.perfMap = perfMap;\n})(window, document);\nperfMap.init();'] + } + } + }, + concat: { + options: { + stripBanners: true + }, + dist: { + src: 'src/**/*.js', + dest: 'build/perfmap.js' + } + }, + jshint: { + src: ['src/**/*.js'], + options: { + jshintrc: '.jshintrc' + } + }, + jasmine: { + // Your project's source files + src: 'src/**/*.js', + options: { + + // Your Jasmine spec files + specs: 'specs/**/*spec.js', + // Your spec helper files + helpers: 'helper/*.js' + // etc. + } + }, + lenient: { + src: 'build/perfmap.js', + dest: 'build/perfmap.js' + } + }); + + // Register tasks. + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-wrap'); + grunt.loadNpmTasks('grunt-lenient'); + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-jasmine'); + + // Default task. + grunt.registerTask('default', ['jshint', 'jasmine', 'concat:dist']); + grunt.registerTask('build', ['concat:dist', 'lenient', 'wrap:basic']); + +}; \ No newline at end of file diff --git a/build/perfmap.js b/build/perfmap.js new file mode 100644 index 0000000..009b009 --- /dev/null +++ b/build/perfmap.js @@ -0,0 +1,256 @@ + +;(function(window, document, undefined){ +'use strict'; + +var perfMap = {}, + loading, + loaded, + gZeroLeft = 0, + gZeroTop = 0, + hArr = [{ + threashold: 0.16, + value: "#1a9850" + }, { + threashold: 0.32, + value: "#66bd63" + }, { + threashold: 0.48, + value: "#a6d964" + }, { + threashold: 0.64, + value: "#fdae61" + }, { + threashold: 0.8, + value: "#f46d43" + }, { + threashold: 1.1, + value: "#d73027" + }]; +function findImages() { + var tags = document.getElementsByTagName('*'), + images = document.getElementsByTagName('img'), + el, + imgs = []; + //re = /url\(([http].*)\)/ig; + //re = /(url)\((.*?)\)/ig; + + imgs = getTagImages(document); + + for (var i = 0, len = tags.length; i < len; i++) { + //imgs.push(getBgElement(tags[i])); + var el = getBgElement(tags[i]); + if (!!el && !!el.src) { + var match = el.src.match(/\((.*?)\)/); + if (match[1]) { + el.src = match[1].replace(/('|")/g, ''); + /*if (style['visibility'] == "hidden") { + hasImage = 0; + } else { + hasImage = 1; + if (elem.tagName == 'BODY') { + body = 1; + } + imgs.push(el); + }*/ + imgs.push(el); + } + } + } + + + //console.log(imgs); + for (var i = 0, len = imgs.length; i < len; i++) { + var entry = window.performance.getEntriesByName(imgs[i].src)[0]; + if (entry) { + var xy = getCumulativeOffset(imgs[i].element, imgs[i].src); + var wh = imgs[i].element.getBoundingClientRect(); + var width = wh.width; + var height = wh.height; + if (width > 10) { + if (height > 10) { + placeMarker(xy, width, height, entry, imgs[i].element.tagName === 'BODY', imgs[i].src); + } + } + } + } +} + +function getBgElement(el) { + /*jshint sub: true */ + //console.log(el.className); + var style = el.currentStyle || window.getComputedStyle(el, false); + //console.log(style.getPropertyValue('background-image')); + if (style.getPropertyValue('background-image') !== 'none') { + return { + element: el, + src: style.getPropertyValue('background-image') + }; + } + return null; +} +function getCumulativeOffset(obj, url) { + var left, top; + left = top = 0; + if (obj.offsetParent) { + do { + left += obj.offsetLeft; + top += obj.offsetTop; + } while (obj = obj.offsetParent); + } + if (0 === top) { + left += gZeroLeft; + top += gZeroTop; + } + return { + left: left, + top: top, + }; +} + + +function getTagImages(document) { + var images = document.getElementsByTagName('img'), + imgs = []; + for (var i = 0; i < images.length; i++) { + if (!!images[i].src) { + imgs.push({ + element: images[i], + src: images[i].src + }); + } + } + return imgs; +} +function heatmap(heat) { + function findIndex(array, predicate) { + var index = -1, + length = array ? array.length : 0; + while (++index < length) { + if (predicate(array[index], index, array)) { + return array[index]; + } + } + return array[length - 1]; + } + + return findIndex(hArr, function(chr) { + return heat < chr.threashold; + }); +} +perfMap.init = function() { + startLoading(); + // build heatmap + findImages(); + + // remove loading message + loading.remove(); + + // mouse events to move timeline around on hover + var elements = document.getElementsByClassName("perfmap"); + var timeline = document.getElementById('perfmap-timeline'); + var timelineLeft; + for (var i = 0, len = elements.length; i < len; i++) { + elements[i].onmouseover = function() { + timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); + if (this.dataset.body != "1") { + this.style.opacity = 1; + } + timeline.style.cssText = "opacity:1; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + }; + elements[i].onmouseout = function() { + timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); + if (this.dataset.body != "1") { + this.style.opacity = 0.925; + } + timeline.style.cssText = "opacity:0; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + }; + } +}; + function placeMarker(xy, width, height, entry, body, url) { + var heat = (entry.responseEnd / loaded), + marker = document.createElement("div"), + padding = 9, + size = 18, + paddingTop, + opacity = 0.925, + align = "center", + bodyText = ""; + + // adjust size of fonts/padding based on width of overlay + if (width < 170) { + padding = 12; + size = 12; + } else if (width > 400) { + padding = 13; + size = 26; + } + + // check for overlay that matches viewport and assume it's like a background image on body + if ((width === document.documentElement.clientWidth) && (height >= document.documentElement.clientHeight)) { + body = true; + } + + // adjust opacity if it's the body element and position label top right + paddingTop = (height / 2) - padding; + if (!!body) { + opacity = 0.6; + size = 18; + align = "right"; + paddingTop = 10; + bodyText = "BODY "; + } + + marker.className = "perfmap"; + marker.setAttribute("data-ms", parseInt(entry.responseEnd)); + marker.setAttribute("data-body", (body ? 1 : 0)); + marker.style.cssText = "position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: " + size + "px; font-weight:800; font-family:\"Helvetica Neue\",sans-serif; text-align:" + align + "; opacity: " + opacity + "; background: " + heatmap(heat).value + "; top: " + xy.top + "px; left: " + xy.left + "px; width: " + width + "px; height:" + height + "px; padding-top:" + paddingTop + "px; z-index: 4000;"; + if (width > 50) { + if (height > 15) { + marker.innerHTML = bodyText + parseInt(entry.responseEnd) + "ms (" + parseInt(entry.duration) + "ms)"; + } + } + document.body.appendChild(marker); + } +function startLoading() { + + // give visual feedback asap + loading = document.createElement("div"); + loading.id = "perfmap-loading"; + loading.innerHTML = "Creating PerfMap"; + loading.style.cssText = "position:absolute; z-index:6000; left:40%; top:45%; background-color:#000; color:#fff; padding:20px 30px; font-family:\"Helvetica Neue\",sans-serif; font-size:24px; font-weight:800;border:2px solid white;"; + document.body.appendChild(loading); + + // get full page load time to calculate heatmap max + loaded = window.performance.timing.loadEventEnd - window.performance.timing.navigationStart; + + // backend + var backend = window.performance.timing.responseEnd - window.performance.timing.navigationStart; + var backendLeft = (backend / loaded) * 100; + var paint, firstPaint, firstPaintLeft; + + // first paint in chrome from https://github.com/addyosmani/timing.js + if (window.chrome && window.chrome.loadTimes) { + paint = window.chrome.loadTimes().firstPaintTime * 1000; + firstPaint = paint - (window.chrome.loadTimes().startLoadTime * 1000); + firstPaintLeft = (firstPaint / loaded) * 100; + } + + // remove any exisiting "perfmap" divs on second click + var elements = document.getElementsByClassName("perfmap"); + while (elements.length > 0) { + elements[0].parentNode.removeChild(elements[0]); + } + + // build bottom legend + var perfmap = document.createElement("div"); + perfmap.id = "perfmap"; + perfmap.style.cssText = "position: fixed; width:100%; bottom:0; left:0; z-index:5000; height: 25px; color:#fff; font-family:\"Helvetica Neue\",sans-serif; font-size:14px; font-weight:800; line-height:14px;"; + perfmap.innerHTML = "
Fully Loaded " + parseInt(loaded) + "ms
First Paint " + parseInt(firstPaint) + "ms
"; + document.body.appendChild(perfmap); + + +} + +window.perfMap = perfMap; +})(window, document); +perfMap.init(); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..b412c19 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "perfMap", + "version": "0.0.1", + "devDependencies": { + "grunt": "^0.4.5", + "grunt-contrib-concat": "^0.5.0", + "grunt-contrib-jasmine": "^0.8.0", + "grunt-contrib-jshint": "^0.10.0", + "grunt-lenient": "0.0.2", + "grunt-wrap": "^0.3.0", + "phantomjs": "^1.9.11" + } +} diff --git a/specs/getBgImages.spec.js b/specs/getBgImages.spec.js new file mode 100644 index 0000000..eba102b --- /dev/null +++ b/specs/getBgImages.spec.js @@ -0,0 +1,80 @@ +'use strict'; +describe('getBgElement spec', function() { + var dummyDOM = '
' + + 'some alt' + + 'some alt' + + 'some alt' + + '' + + '
'; + + + + it("should return 1 element with same src url", function() { + var div = document.createElement("div"); + div.style.width = "100px"; + div.style.height = "100px"; + div.style.background = "url(http://somePath.com/jasmine.jpg)"; + div.style.color = "white"; + div.innerHTML = "Hello"; + + document.body.appendChild(div); + + var result = getBgElement(div); + expect(result.element).toEqual(div); + expect(result.src).toEqual(div.style.background); + }); + + it("should return null when has backgroung-image = none", function() { + var div = document.createElement("div"); + div.style.width = "100px"; + div.style.height = "100px"; + div.style.background = "none"; + div.style.color = "white"; + div.innerHTML = "Hello"; + + document.body.appendChild(div); + + var result = getBgElement(div); + expect(result).toEqual(null); + }); + it("should return null when has no backgroung", function() { + var div = document.createElement("div"); + div.style.width = "100px"; + div.style.height = "100px"; + + div.style.color = "white"; + div.innerHTML = "Hello"; + + document.body.appendChild(div); + + var result = getBgElement(div); + expect(result).toEqual(null); + }); + + it("should return null when has no backgroung", function() { + var css = '.gb_Fa { background-image: url("https://somePath.com/i1_e9f91fe3.png"); background-size: 247px 204px;}', + head = document.head || document.getElementsByTagName('head')[0], + style = document.createElement('style'); + style.type = 'text/css'; + if (style.styleSheet) { + style.styleSheet.cssText = css; + } else { + style.appendChild(document.createTextNode(css)); + } + + head.appendChild(style); + + + var div = document.createElement("div"); + div.className = "gb_Fa"; + + + document.body.appendChild(div); + + var result = getBgElement(div); + expect(result.element).toEqual(div); + expect(result.src).toEqual('url(https://somePath.com/i1_e9f91fe3.png)'); + }); + + +}); \ No newline at end of file diff --git a/specs/test.spec.js b/specs/test.spec.js new file mode 100644 index 0000000..c38e63f --- /dev/null +++ b/specs/test.spec.js @@ -0,0 +1,53 @@ +'use strict'; +describe('getTagImages spec', function() { + + + + + it("should return 3 images", function() { + var dummyDOM = '
' + + 'some alt' + + 'some alt' + + 'some alt' + + '' + + '
'; + document.body.innerHTML = dummyDOM; + var result = getTagImages(document); + expect(result.length).toEqual(3); + }); + + it("should return 0 images", function() { + document.body.innerHTML = '
'; + var result = getTagImages(document); + expect(result).toEqual([]); + }); + + it("should return 3 images with different extensions", function() { + var dummyDOM = '
' + + 'some alt' + + 'some alt' + + 'some alt' + + '' + + '
'; + document.body.innerHTML = dummyDOM; + var result = getTagImages(document); + expect(result[0].src).toEqual("http://somePath.com/jasmine.png"); + expect(result[1].src).toEqual("http://somePath.com/jasmine.jpg"); + expect(result[2].src).toEqual("http://somePath.com/jasmine.gif"); + //done(); + }); + + it("should return 0 images when it has no src attribute ", function() { + var dummyDOM = '
' + + 'some alt' + + '' + + '
'; + document.body.innerHTML = dummyDOM; + var result = getTagImages(document); + expect(result.length).toEqual(0); + //done(); + }); + + + +}); \ No newline at end of file diff --git a/src/_vars.js b/src/_vars.js new file mode 100644 index 0000000..2b96332 --- /dev/null +++ b/src/_vars.js @@ -0,0 +1,24 @@ +var perfMap = {}, + loading, + loaded, + gZeroLeft = 0, + gZeroTop = 0, + hArr = [{ + threashold: 0.16, + value: '#1a9850' + }, { + threashold: 0.32, + value: '#66bd63' + }, { + threashold: 0.48, + value: '#a6d964' + }, { + threashold: 0.64, + value: '#fdae61' + }, { + threashold: 0.8, + value: '#f46d43' + }, { + threashold: 1.1, + value: '#d73027' + }]; \ No newline at end of file diff --git a/src/findImages.js b/src/findImages.js new file mode 100644 index 0000000..49b5fed --- /dev/null +++ b/src/findImages.js @@ -0,0 +1,50 @@ +'use strict'; + +function findImages() { + var tags = document.getElementsByTagName('*'), + images = document.getElementsByTagName('img'), + el, + imgs = []; + //re = /url\(([http].*)\)/ig; + //re = /(url)\((.*?)\)/ig; + + imgs = getTagImages(document); + len = tags.length; + for (var j = 0; j < len; j++) { + el = getBgElement(tags[j]); + if (!!el && !!el.src) { + var match = el.src.match(/\((.*?)\)/); + if (match[1]) { + el.src = match[1].replace(/('|")/g, ''); + /*if (style['visibility'] == "hidden") { + hasImage = 0; + } else { + hasImage = 1; + if (elem.tagName == 'BODY') { + body = 1; + } + imgs.push(el); + }*/ + imgs.push(el); + } + } + } + + + //console.log(imgs); + len = imgs.length; + for (var i = 0; i < len; i++) { + var entry = window.performance.getEntriesByName(imgs[i].src)[0]; + if (entry) { + var xy = getCumulativeOffset(imgs[i].element, imgs[i].src); + var wh = imgs[i].element.getBoundingClientRect(); + var width = wh.width; + var height = wh.height; + if (width > 10) { + if (height > 10) { + placeMarker(xy, width, height, entry, imgs[i].element.tagName === 'BODY', imgs[i].src); + } + } + } + } +} \ No newline at end of file diff --git a/src/getBgImages.js b/src/getBgImages.js new file mode 100644 index 0000000..178f576 --- /dev/null +++ b/src/getBgImages.js @@ -0,0 +1,14 @@ +'use strict'; +function getBgElement(el) { + /*jshint sub: true */ + //console.log(el.className); + var style = el.currentStyle || window.getComputedStyle(el, false); + //console.log(style.getPropertyValue('background-image')); + if (style.getPropertyValue('background-image') !== 'none') { + return { + element: el, + src: style.getPropertyValue('background-image') + }; + } + return null; +} \ No newline at end of file diff --git a/src/getCumulativeOffset.js b/src/getCumulativeOffset.js new file mode 100644 index 0000000..2622add --- /dev/null +++ b/src/getCumulativeOffset.js @@ -0,0 +1,20 @@ +'use strict'; + +function getCumulativeOffset(obj, url) { + var left, top; + left = top = 0; + if (obj.offsetParent) { + do { + left += obj.offsetLeft; + top += obj.offsetTop; + } while (obj = obj.offsetParent); + } + if (0 === top) { + left += gZeroLeft; + top += gZeroTop; + } + return { + left: left, + top: top, + }; +} \ No newline at end of file diff --git a/src/getTagImages.js b/src/getTagImages.js new file mode 100644 index 0000000..deb3fb7 --- /dev/null +++ b/src/getTagImages.js @@ -0,0 +1,15 @@ +'use strict'; + +function getTagImages(document) { + var images = document.getElementsByTagName('img'), + imgs = []; + for (var i = 0; i < images.length; i++) { + if (!!images[i].src) { + imgs.push({ + element: images[i], + src: images[i].src + }); + } + } + return imgs; +} \ No newline at end of file diff --git a/src/heatmap.js b/src/heatmap.js new file mode 100644 index 0000000..2d27fcb --- /dev/null +++ b/src/heatmap.js @@ -0,0 +1,18 @@ +'use strict'; + +function heatmap(heat) { + function findIndex(array, predicate) { + var index = -1, + length = array ? array.length : 0; + while (++index < length) { + if (predicate(array[index], index, array)) { + return array[index]; + } + } + return array[length - 1]; + } + + return findIndex(hArr, function(chr) { + return heat < chr.threashold; + }); +} \ No newline at end of file diff --git a/src/init.js b/src/init.js new file mode 100644 index 0000000..13304e9 --- /dev/null +++ b/src/init.js @@ -0,0 +1,31 @@ +'use strict'; + +perfMap.init = function() { + startLoading(); + // build heatmap + findImages(); + + // remove loading message + loading.remove(); + + // mouse events to move timeline around on hover + var elements = document.getElementsByClassName("perfmap"); + var timeline = document.getElementById('perfmap-timeline'); + var timelineLeft; + for (var i = 0, len = elements.length; i < len; i++) { + elements[i].onmouseover = function() { + timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); + if (this.dataset.body != "1") { + this.style.opacity = 1; + } + timeline.style.cssText = "opacity:1; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + }; + elements[i].onmouseout = function() { + timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); + if (this.dataset.body != "1") { + this.style.opacity = 0.925; + } + timeline.style.cssText = "opacity:0; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + }; + } +}; \ No newline at end of file diff --git a/src/placeMarker.js b/src/placeMarker.js new file mode 100644 index 0000000..4c1c25c --- /dev/null +++ b/src/placeMarker.js @@ -0,0 +1,47 @@ +'use strict'; + +function placeMarker(xy, width, height, entry, body, url) { + var heat = (entry.responseEnd / loaded), + marker = document.createElement("div"), + padding = 9, + size = 18, + paddingTop, + opacity = 0.925, + align = "center", + bodyText = ""; + + // adjust size of fonts/padding based on width of overlay + if (width < 170) { + padding = 12; + size = 12; + } else if (width > 400) { + padding = 13; + size = 26; + } + + // check for overlay that matches viewport and assume it's like a background image on body + if ((width === document.documentElement.clientWidth) && (height >= document.documentElement.clientHeight)) { + body = true; + } + + // adjust opacity if it's the body element and position label top right + paddingTop = (height / 2) - padding; + if (!!body) { + opacity = 0.6; + size = 18; + align = "right"; + paddingTop = 10; + bodyText = "BODY "; + } + + marker.className = "perfmap"; + marker.setAttribute("data-ms", parseInt(entry.responseEnd)); + marker.setAttribute("data-body", (body ? 1 : 0)); + marker.style.cssText = "position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: " + size + "px; font-weight:800; font-family:\"Helvetica Neue\",sans-serif; text-align:" + align + "; opacity: " + opacity + "; background: " + heatmap(heat).value + "; top: " + xy.top + "px; left: " + xy.left + "px; width: " + width + "px; height:" + height + "px; padding-top:" + paddingTop + "px; z-index: 4000;"; + if (width > 50) { + if (height > 15) { + marker.innerHTML = bodyText + parseInt(entry.responseEnd) + "ms (" + parseInt(entry.duration) + "ms)"; + } + } + document.body.appendChild(marker); +} \ No newline at end of file diff --git a/src/startLoading.js b/src/startLoading.js new file mode 100644 index 0000000..948c297 --- /dev/null +++ b/src/startLoading.js @@ -0,0 +1,41 @@ +'use strict'; + +function startLoading() { + + // give visual feedback asap + loading = document.createElement('div'); + loading.id = 'perfmap-loading'; + loading.innerHTML = 'Creating PerfMap'; + loading.style.cssText = 'position:absolute; z-index:6000; left:40%; top:45%; background-color:#000; color:#fff; padding:20px 30px; font-family:"Helvetica Neue",sans-serif; font-size:24px; font-weight:800;border:2px solid white;'; + document.body.appendChild(loading); + + // get full page load time to calculate heatmap max + loaded = window.performance.timing.loadEventEnd - window.performance.timing.navigationStart; + + // backend + var backend = window.performance.timing.responseEnd - window.performance.timing.navigationStart; + var backendLeft = (backend / loaded) * 100; + var paint, firstPaint, firstPaintLeft; + + // first paint in chrome from https://github.com/addyosmani/timing.js + if (window.chrome && window.chrome.loadTimes) { + paint = window.chrome.loadTimes().firstPaintTime * 1000; + firstPaint = paint - (window.chrome.loadTimes().startLoadTime * 1000); + firstPaintLeft = (firstPaint / loaded) * 100; + } + + // remove any exisiting "perfmap" divs on second click + var elements = document.getElementsByClassName('perfmap'); + while (elements.length > 0) { + elements[0].parentNode.removeChild(elements[0]); + } + + // build bottom legend + var perfmap = document.createElement('div'); + perfmap.id = 'perfmap'; + perfmap.style.cssText = 'position: fixed; width:100%; bottom:0; left:0; z-index:5000; height: 25px; color:#fff; font-family:"Helvetica Neue",sans-serif; font-size:14px; font-weight:800; line-height:14px;'; + perfmap.innerHTML = '
Fully Loaded ' + parseInt(loaded) + 'ms
First Paint ' + parseInt(firstPaint) + 'ms
'; + document.body.appendChild(perfmap); + + +} \ No newline at end of file From 2f1121d7a20cb7035432dc05078bb5382a52d879 Mon Sep 17 00:00:00 2001 From: Matias Carranza Date: Tue, 21 Oct 2014 20:06:19 -0300 Subject: [PATCH 4/9] Rename test.spec.js to getTagImages.spec.js --- specs/{test.spec.js => getTagImages.spec.js} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename specs/{test.spec.js => getTagImages.spec.js} (99%) diff --git a/specs/test.spec.js b/specs/getTagImages.spec.js similarity index 99% rename from specs/test.spec.js rename to specs/getTagImages.spec.js index c38e63f..4ac8a1b 100644 --- a/specs/test.spec.js +++ b/specs/getTagImages.spec.js @@ -50,4 +50,4 @@ describe('getTagImages spec', function() { -}); \ No newline at end of file +}); From d36611bcde09ae471b1f02bca01e4e121b0a00fb Mon Sep 17 00:00:00 2001 From: Matias Carranza Date: Tue, 21 Oct 2014 20:16:50 -0300 Subject: [PATCH 5/9] fix issue with specs timeout issue when running tests was because of document.body.innerHTML = --- specs/getTagImages.spec.js | 53 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/specs/getTagImages.spec.js b/specs/getTagImages.spec.js index 4ac8a1b..8e80f53 100644 --- a/specs/getTagImages.spec.js +++ b/specs/getTagImages.spec.js @@ -1,53 +1,54 @@ 'use strict'; describe('getTagImages spec', function() { - - - + var container = document.createElement("div"); + container.id = 'container'; + document.body.appendChild(container); + beforeEach(function() { + document.getElementById('container'); + container.innerHTML = ''; + }); + it("should return 3 images", function() { - var dummyDOM = '
' + - 'some alt' + - 'some alt' + - 'some alt' + - '' + - '
'; - document.body.innerHTML = dummyDOM; + container.innerHTML = '
' + + 'some alt' + + 'some alt' + + 'some alt' + + '' + + '
'; var result = getTagImages(document); expect(result.length).toEqual(3); }); it("should return 0 images", function() { - document.body.innerHTML = '
'; + var div = document.createElement("div"); + container.appendChild(div); var result = getTagImages(document); expect(result).toEqual([]); }); it("should return 3 images with different extensions", function() { - var dummyDOM = '
' + - 'some alt' + - 'some alt' + - 'some alt' + - '' + - '
'; - document.body.innerHTML = dummyDOM; + container.innerHTML = '
' + + 'some alt' + + 'some alt' + + 'some alt' + + '' + + '
'; var result = getTagImages(document); expect(result[0].src).toEqual("http://somePath.com/jasmine.png"); expect(result[1].src).toEqual("http://somePath.com/jasmine.jpg"); expect(result[2].src).toEqual("http://somePath.com/jasmine.gif"); - //done(); }); it("should return 0 images when it has no src attribute ", function() { - var dummyDOM = '
' + - 'some alt' + - '' + - '
'; - document.body.innerHTML = dummyDOM; + container.innerHTML = '
' + + 'some alt' + + '' + + '
'; var result = getTagImages(document); expect(result.length).toEqual(0); - //done(); }); -}); +}); \ No newline at end of file From 7212f5e7563dfafa2c794998a61c0e76abed1f91 Mon Sep 17 00:00:00 2001 From: Matias Carranza Date: Tue, 21 Oct 2014 20:21:53 -0300 Subject: [PATCH 6/9] remove unused conf --- Gruntfile.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index d18a05e..b6cbf2a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -31,12 +31,7 @@ module.exports = function(grunt) { // Your project's source files src: 'src/**/*.js', options: { - - // Your Jasmine spec files - specs: 'specs/**/*spec.js', - // Your spec helper files - helpers: 'helper/*.js' - // etc. + specs: 'specs/**/*spec.js' } }, lenient: { @@ -54,6 +49,7 @@ module.exports = function(grunt) { // Default task. grunt.registerTask('default', ['jshint', 'jasmine', 'concat:dist']); + grunt.registerTask('test', ['jasmine']); grunt.registerTask('build', ['concat:dist', 'lenient', 'wrap:basic']); }; \ No newline at end of file From a96561e643f023d6d5786be363355d4cf9e8a8ae Mon Sep 17 00:00:00 2001 From: Matias Carranza Date: Tue, 21 Oct 2014 20:37:06 -0300 Subject: [PATCH 7/9] fixing jshint issues --- Gruntfile.js | 7 ++++--- src/findImages.js | 1 + src/init.js | 10 +++++----- src/placeMarker.js | 20 ++++++++++---------- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index b6cbf2a..75a375c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -22,7 +22,8 @@ module.exports = function(grunt) { } }, jshint: { - src: ['src/**/*.js'], + //src: ['src/**/*.js'], + src: ['build/perfmap.js'], options: { jshintrc: '.jshintrc' } @@ -48,8 +49,8 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-jasmine'); // Default task. - grunt.registerTask('default', ['jshint', 'jasmine', 'concat:dist']); + grunt.registerTask('default', ['jasmine', 'concat:dist', 'jshint']); grunt.registerTask('test', ['jasmine']); - grunt.registerTask('build', ['concat:dist', 'lenient', 'wrap:basic']); + grunt.registerTask('build', ['concat:dist', 'lenient', 'wrap:basic', 'jasmine', 'jshint']); }; \ No newline at end of file diff --git a/src/findImages.js b/src/findImages.js index 49b5fed..b2c2183 100644 --- a/src/findImages.js +++ b/src/findImages.js @@ -4,6 +4,7 @@ function findImages() { var tags = document.getElementsByTagName('*'), images = document.getElementsByTagName('img'), el, + len, imgs = []; //re = /url\(([http].*)\)/ig; //re = /(url)\((.*?)\)/ig; diff --git a/src/init.js b/src/init.js index 13304e9..4458153 100644 --- a/src/init.js +++ b/src/init.js @@ -9,23 +9,23 @@ perfMap.init = function() { loading.remove(); // mouse events to move timeline around on hover - var elements = document.getElementsByClassName("perfmap"); + var elements = document.getElementsByClassName('perfmap'); var timeline = document.getElementById('perfmap-timeline'); var timelineLeft; for (var i = 0, len = elements.length; i < len; i++) { elements[i].onmouseover = function() { timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); - if (this.dataset.body != "1") { + if (this.dataset.body !=='1') { this.style.opacity = 1; } - timeline.style.cssText = "opacity:1; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + timeline.style.cssText = 'opacity:1; transition: 0.5s ease-in-out; transform: translate(' + parseInt(timelineLeft) + 'px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;'; }; elements[i].onmouseout = function() { timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); - if (this.dataset.body != "1") { + if (this.dataset.body !== '1') { this.style.opacity = 0.925; } - timeline.style.cssText = "opacity:0; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + timeline.style.cssText = 'opacity:0; transition: 0.5s ease-in-out; transform: translate(' + parseInt(timelineLeft) + 'px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;'; }; } }; \ No newline at end of file diff --git a/src/placeMarker.js b/src/placeMarker.js index 4c1c25c..244857c 100644 --- a/src/placeMarker.js +++ b/src/placeMarker.js @@ -2,13 +2,13 @@ function placeMarker(xy, width, height, entry, body, url) { var heat = (entry.responseEnd / loaded), - marker = document.createElement("div"), + marker = document.createElement('div'), padding = 9, size = 18, paddingTop, opacity = 0.925, - align = "center", - bodyText = ""; + align = 'center', + bodyText = ''; // adjust size of fonts/padding based on width of overlay if (width < 170) { @@ -29,18 +29,18 @@ function placeMarker(xy, width, height, entry, body, url) { if (!!body) { opacity = 0.6; size = 18; - align = "right"; + align = 'right'; paddingTop = 10; - bodyText = "BODY "; + bodyText = 'BODY '; } - marker.className = "perfmap"; - marker.setAttribute("data-ms", parseInt(entry.responseEnd)); - marker.setAttribute("data-body", (body ? 1 : 0)); - marker.style.cssText = "position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: " + size + "px; font-weight:800; font-family:\"Helvetica Neue\",sans-serif; text-align:" + align + "; opacity: " + opacity + "; background: " + heatmap(heat).value + "; top: " + xy.top + "px; left: " + xy.left + "px; width: " + width + "px; height:" + height + "px; padding-top:" + paddingTop + "px; z-index: 4000;"; + marker.className = 'perfmap'; + marker.setAttribute('data-ms', parseInt(entry.responseEnd)); + marker.setAttribute('data-body', (body ? 1 : 0)); + marker.style.cssText = 'position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: ' + size + 'px; font-weight:800; font-family:"Helvetica Neue",sans-serif; text-align:' + align + '; opacity: ' + opacity + '; background: ' + heatmap(heat).value + '; top: ' + xy.top + 'px; left: ' + xy.left + 'px; width: ' + width + 'px; height:' + height + 'px; padding-top:' + paddingTop + 'px; z-index: 4000;'; if (width > 50) { if (height > 15) { - marker.innerHTML = bodyText + parseInt(entry.responseEnd) + "ms (" + parseInt(entry.duration) + "ms)"; + marker.innerHTML = bodyText + parseInt(entry.responseEnd) + 'ms (' + parseInt(entry.duration) + 'ms)'; } } document.body.appendChild(marker); From 95cb10e454ea99bc5f5c568b7d0b872fd61fbcc6 Mon Sep 17 00:00:00 2001 From: Matias Carranza Date: Tue, 21 Oct 2014 20:47:49 -0300 Subject: [PATCH 8/9] perfmap after grunt build --- build/perfmap.js | 143 ++++++++++++++++++++++++++--------------------- 1 file changed, 78 insertions(+), 65 deletions(-) diff --git a/build/perfmap.js b/build/perfmap.js index 009b009..27fef59 100644 --- a/build/perfmap.js +++ b/build/perfmap.js @@ -9,36 +9,38 @@ var perfMap = {}, gZeroTop = 0, hArr = [{ threashold: 0.16, - value: "#1a9850" + value: '#1a9850' }, { threashold: 0.32, - value: "#66bd63" + value: '#66bd63' }, { threashold: 0.48, - value: "#a6d964" + value: '#a6d964' }, { threashold: 0.64, - value: "#fdae61" + value: '#fdae61' }, { threashold: 0.8, - value: "#f46d43" + value: '#f46d43' }, { threashold: 1.1, - value: "#d73027" + value: '#d73027' }]; + + function findImages() { var tags = document.getElementsByTagName('*'), images = document.getElementsByTagName('img'), el, + len, imgs = []; //re = /url\(([http].*)\)/ig; //re = /(url)\((.*?)\)/ig; imgs = getTagImages(document); - - for (var i = 0, len = tags.length; i < len; i++) { - //imgs.push(getBgElement(tags[i])); - var el = getBgElement(tags[i]); + len = tags.length; + for (var j = 0; j < len; j++) { + el = getBgElement(tags[j]); if (!!el && !!el.src) { var match = el.src.match(/\((.*?)\)/); if (match[1]) { @@ -59,7 +61,8 @@ function findImages() { //console.log(imgs); - for (var i = 0, len = imgs.length; i < len; i++) { + len = imgs.length; + for (var i = 0; i < len; i++) { var entry = window.performance.getEntriesByName(imgs[i].src)[0]; if (entry) { var xy = getCumulativeOffset(imgs[i].element, imgs[i].src); @@ -88,6 +91,8 @@ function getBgElement(el) { } return null; } + + function getCumulativeOffset(obj, url) { var left, top; left = top = 0; @@ -121,6 +126,8 @@ function getTagImages(document) { } return imgs; } + + function heatmap(heat) { function findIndex(array, predicate) { var index = -1, @@ -137,6 +144,8 @@ function heatmap(heat) { return heat < chr.threashold; }); } + + perfMap.init = function() { startLoading(); // build heatmap @@ -146,78 +155,82 @@ perfMap.init = function() { loading.remove(); // mouse events to move timeline around on hover - var elements = document.getElementsByClassName("perfmap"); + var elements = document.getElementsByClassName('perfmap'); var timeline = document.getElementById('perfmap-timeline'); var timelineLeft; for (var i = 0, len = elements.length; i < len; i++) { elements[i].onmouseover = function() { timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); - if (this.dataset.body != "1") { + if (this.dataset.body !=='1') { this.style.opacity = 1; } - timeline.style.cssText = "opacity:1; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + timeline.style.cssText = 'opacity:1; transition: 0.5s ease-in-out; transform: translate(' + parseInt(timelineLeft) + 'px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;'; }; elements[i].onmouseout = function() { timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); - if (this.dataset.body != "1") { + if (this.dataset.body !== '1') { this.style.opacity = 0.925; } - timeline.style.cssText = "opacity:0; transition: 0.5s ease-in-out; transform: translate(" + parseInt(timelineLeft) + "px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;"; + timeline.style.cssText = 'opacity:0; transition: 0.5s ease-in-out; transform: translate(' + parseInt(timelineLeft) + 'px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;'; }; } }; - function placeMarker(xy, width, height, entry, body, url) { - var heat = (entry.responseEnd / loaded), - marker = document.createElement("div"), - padding = 9, - size = 18, - paddingTop, - opacity = 0.925, - align = "center", - bodyText = ""; - - // adjust size of fonts/padding based on width of overlay - if (width < 170) { - padding = 12; - size = 12; - } else if (width > 400) { - padding = 13; - size = 26; - } - // check for overlay that matches viewport and assume it's like a background image on body - if ((width === document.documentElement.clientWidth) && (height >= document.documentElement.clientHeight)) { - body = true; - } - // adjust opacity if it's the body element and position label top right - paddingTop = (height / 2) - padding; - if (!!body) { - opacity = 0.6; - size = 18; - align = "right"; - paddingTop = 10; - bodyText = "BODY "; - } +function placeMarker(xy, width, height, entry, body, url) { + var heat = (entry.responseEnd / loaded), + marker = document.createElement('div'), + padding = 9, + size = 18, + paddingTop, + opacity = 0.925, + align = 'center', + bodyText = ''; - marker.className = "perfmap"; - marker.setAttribute("data-ms", parseInt(entry.responseEnd)); - marker.setAttribute("data-body", (body ? 1 : 0)); - marker.style.cssText = "position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: " + size + "px; font-weight:800; font-family:\"Helvetica Neue\",sans-serif; text-align:" + align + "; opacity: " + opacity + "; background: " + heatmap(heat).value + "; top: " + xy.top + "px; left: " + xy.left + "px; width: " + width + "px; height:" + height + "px; padding-top:" + paddingTop + "px; z-index: 4000;"; - if (width > 50) { - if (height > 15) { - marker.innerHTML = bodyText + parseInt(entry.responseEnd) + "ms (" + parseInt(entry.duration) + "ms)"; - } + // adjust size of fonts/padding based on width of overlay + if (width < 170) { + padding = 12; + size = 12; + } else if (width > 400) { + padding = 13; + size = 26; + } + + // check for overlay that matches viewport and assume it's like a background image on body + if ((width === document.documentElement.clientWidth) && (height >= document.documentElement.clientHeight)) { + body = true; + } + + // adjust opacity if it's the body element and position label top right + paddingTop = (height / 2) - padding; + if (!!body) { + opacity = 0.6; + size = 18; + align = 'right'; + paddingTop = 10; + bodyText = 'BODY '; + } + + marker.className = 'perfmap'; + marker.setAttribute('data-ms', parseInt(entry.responseEnd)); + marker.setAttribute('data-body', (body ? 1 : 0)); + marker.style.cssText = 'position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: ' + size + 'px; font-weight:800; font-family:"Helvetica Neue",sans-serif; text-align:' + align + '; opacity: ' + opacity + '; background: ' + heatmap(heat).value + '; top: ' + xy.top + 'px; left: ' + xy.left + 'px; width: ' + width + 'px; height:' + height + 'px; padding-top:' + paddingTop + 'px; z-index: 4000;'; + if (width > 50) { + if (height > 15) { + marker.innerHTML = bodyText + parseInt(entry.responseEnd) + 'ms (' + parseInt(entry.duration) + 'ms)'; } - document.body.appendChild(marker); } + document.body.appendChild(marker); +} + + function startLoading() { // give visual feedback asap - loading = document.createElement("div"); - loading.id = "perfmap-loading"; - loading.innerHTML = "Creating PerfMap"; - loading.style.cssText = "position:absolute; z-index:6000; left:40%; top:45%; background-color:#000; color:#fff; padding:20px 30px; font-family:\"Helvetica Neue\",sans-serif; font-size:24px; font-weight:800;border:2px solid white;"; + loading = document.createElement('div'); + loading.id = 'perfmap-loading'; + loading.innerHTML = 'Creating PerfMap'; + loading.style.cssText = 'position:absolute; z-index:6000; left:40%; top:45%; background-color:#000; color:#fff; padding:20px 30px; font-family:"Helvetica Neue",sans-serif; font-size:24px; font-weight:800;border:2px solid white;'; document.body.appendChild(loading); // get full page load time to calculate heatmap max @@ -236,16 +249,16 @@ function startLoading() { } // remove any exisiting "perfmap" divs on second click - var elements = document.getElementsByClassName("perfmap"); + var elements = document.getElementsByClassName('perfmap'); while (elements.length > 0) { elements[0].parentNode.removeChild(elements[0]); } // build bottom legend - var perfmap = document.createElement("div"); - perfmap.id = "perfmap"; - perfmap.style.cssText = "position: fixed; width:100%; bottom:0; left:0; z-index:5000; height: 25px; color:#fff; font-family:\"Helvetica Neue\",sans-serif; font-size:14px; font-weight:800; line-height:14px;"; - perfmap.innerHTML = "
Fully Loaded " + parseInt(loaded) + "ms
First Paint " + parseInt(firstPaint) + "ms
"; + var perfmap = document.createElement('div'); + perfmap.id = 'perfmap'; + perfmap.style.cssText = 'position: fixed; width:100%; bottom:0; left:0; z-index:5000; height: 25px; color:#fff; font-family:"Helvetica Neue",sans-serif; font-size:14px; font-weight:800; line-height:14px;'; + perfmap.innerHTML = '
Fully Loaded ' + parseInt(loaded) + 'ms
First Paint ' + parseInt(firstPaint) + 'ms
'; document.body.appendChild(perfmap); From 5b7fef3cb5e156583cba9080f5615f3ddaa9b56d Mon Sep 17 00:00:00 2001 From: Matias Carranza Date: Wed, 22 Oct 2014 18:14:53 -0300 Subject: [PATCH 9/9] change marker from div to background images to avoid bad behavior when images changes position dynamically in a page --- build/perfmap.js | 96 ++++++++++++++++++++++++++------------ src/_vars.js | 18 ++++--- src/findImages.js | 6 ++- src/getBgImages.js | 10 ++-- src/getCumulativeOffset.js | 4 +- src/init.js | 12 +++-- src/placeMarker.js | 44 +++++++++++++---- 7 files changed, 133 insertions(+), 57 deletions(-) diff --git a/build/perfmap.js b/build/perfmap.js index 27fef59..dd3af4c 100644 --- a/build/perfmap.js +++ b/build/perfmap.js @@ -9,22 +9,28 @@ var perfMap = {}, gZeroTop = 0, hArr = [{ threashold: 0.16, - value: '#1a9850' + value: '#1a9850', + rgba: 'rgba(26, 152, 80, 0.95)' }, { threashold: 0.32, - value: '#66bd63' + value: '#66bd63', + rgba: 'rgba(102, 189, 99, 0.95)' }, { threashold: 0.48, - value: '#a6d964' + value: '#a6d964', + rgba: 'rgba(166, 217, 100, 0.95)' }, { threashold: 0.64, - value: '#fdae61' + value: '#fdae61', + rgba: 'rgba(253, 174, 97, 0.95)' }, { threashold: 0.8, - value: '#f46d43' + value: '#f46d43', + rgba: 'rgba(244, 109, 67, 0.95)' }, { threashold: 1.1, - value: '#d73027' + value: '#d73027', + rgba: 'rgba(215, 48, 39, 0.95)' }]; @@ -42,6 +48,8 @@ function findImages() { for (var j = 0; j < len; j++) { el = getBgElement(tags[j]); if (!!el && !!el.src) { + el.bgImg = el.src; + var match = el.src.match(/\((.*?)\)/); if (match[1]) { el.src = match[1].replace(/('|")/g, ''); @@ -65,35 +73,37 @@ function findImages() { for (var i = 0; i < len; i++) { var entry = window.performance.getEntriesByName(imgs[i].src)[0]; if (entry) { - var xy = getCumulativeOffset(imgs[i].element, imgs[i].src); + //var xy = getCumulativeOffset(imgs[i].element, imgs[i].src); var wh = imgs[i].element.getBoundingClientRect(); var width = wh.width; var height = wh.height; if (width > 10) { if (height > 10) { - placeMarker(xy, width, height, entry, imgs[i].element.tagName === 'BODY', imgs[i].src); + placeMarker(width, height, entry, imgs[i].element.tagName === 'BODY', imgs[i].src, imgs[i]); } } } } } + function getBgElement(el) { /*jshint sub: true */ //console.log(el.className); var style = el.currentStyle || window.getComputedStyle(el, false); //console.log(style.getPropertyValue('background-image')); - if (style.getPropertyValue('background-image') !== 'none') { + if (style.getPropertyValue('background-image') !== 'none') { return { - element: el, - src: style.getPropertyValue('background-image') - }; + element: el, + src: style.getPropertyValue('background-image'), + position: style.getPropertyValue('background-position') + }; } return null; } -function getCumulativeOffset(obj, url) { +/*function getCumulativeOffset(obj, url) { var left, top; left = top = 0; if (obj.offsetParent) { @@ -110,7 +120,7 @@ function getCumulativeOffset(obj, url) { left: left, top: top, }; -} +}*/ function getTagImages(document) { @@ -162,22 +172,27 @@ perfMap.init = function() { elements[i].onmouseover = function() { timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); if (this.dataset.body !=='1') { - this.style.opacity = 1; + //this.style.opacity = 1; + this.style.cssText = this.style.cssText.replace(/(\d\.\d*)\)/g, '0.1)'); + timeline.style.cssText = 'opacity:1; transition: 0.5s ease-in-out; transform: translate(' + parseInt(timelineLeft) + 'px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;'; } - timeline.style.cssText = 'opacity:1; transition: 0.5s ease-in-out; transform: translate(' + parseInt(timelineLeft) + 'px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;'; + }; elements[i].onmouseout = function() { timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); if (this.dataset.body !== '1') { - this.style.opacity = 0.925; + //this.style.opacity = 0.925; + this.style.cssText = this.style.cssText.replace(/(\d\.\d*)\)/g, '0.95)'); + timeline.style.cssText = 'opacity:0; transition: 0.5s ease-in-out; transform: translate(' + parseInt(timelineLeft) + 'px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;'; } - timeline.style.cssText = 'opacity:0; transition: 0.5s ease-in-out; transform: translate(' + parseInt(timelineLeft) + 'px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;'; + }; } }; -function placeMarker(xy, width, height, entry, body, url) { +function placeMarker(width, height, entry, body, url, el) { + //background-image: linear-gradient(90deg, rgba(253, 174, 97, 0.95), rgba(253, 174, 97, 0.95)); var heat = (entry.responseEnd / loaded), marker = document.createElement('div'), padding = 9, @@ -210,17 +225,40 @@ function placeMarker(xy, width, height, entry, body, url) { paddingTop = 10; bodyText = 'BODY '; } - - marker.className = 'perfmap'; - marker.setAttribute('data-ms', parseInt(entry.responseEnd)); - marker.setAttribute('data-body', (body ? 1 : 0)); - marker.style.cssText = 'position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: ' + size + 'px; font-weight:800; font-family:"Helvetica Neue",sans-serif; text-align:' + align + '; opacity: ' + opacity + '; background: ' + heatmap(heat).value + '; top: ' + xy.top + 'px; left: ' + xy.left + 'px; width: ' + width + 'px; height:' + height + 'px; padding-top:' + paddingTop + 'px; z-index: 4000;'; - if (width > 50) { - if (height > 15) { - marker.innerHTML = bodyText + parseInt(entry.responseEnd) + 'ms (' + parseInt(entry.duration) + 'ms)'; - } + var elem = el.element; + //debugger; + var oldClass = elem.className; + elem.className = oldClass + ' perfmap'; + elem.setAttribute('data-ms', parseInt(entry.responseEnd)); + elem.setAttribute('data-body', (body ? 1 : 0)); + var oldStyle = elem.style.cssText; + var bgImg = ''; + if (!!el.bgImg) { + bgImg = ', ' + el.bgImg; + } else { + //debugger; + bgImg = ', url("' + elem.src + '") '; + + var style = elem.currentStyle || window.getComputedStyle(elem, false); + //console.log(style.getPropertyValue('background-image')); + oldStyle += 'width: ' + (style.getPropertyValue('width') || width + 'px') + '!important;'; + oldStyle += 'height: ' + ( style.getPropertyValue('height') || height + 'px') + '!important;'; + elem.removeAttribute("src"); } - document.body.appendChild(marker); + var bgPosition = 'background-position: 0px 0px'; + if (!!el.position) { + bgPosition += ', ' + el.position; + } + + elem.style.cssText = oldStyle + ' background-image: linear-gradient(' + heatmap(heat).rgba + ', ' + heatmap(heat).rgba + ')' + bgImg + '; ' + bgPosition + '; background-size: contain;'; + //elem.style.cssText = 'position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: ' + size + 'px; font-weight:800; font-family:"Helvetica Neue",sans-serif; text-align:' + align + '; opacity: ' + opacity + '; background: ' + heatmap(heat).value + '; top: ' + xy.top + 'px; left: ' + xy.left + 'px; width: ' + width + 'px; height:' + height + 'px; padding-top:' + paddingTop + 'px; z-index: 4000;'; + // if (width > 50) { + // if (height > 15) { + // oldStyle = elem.style.cssText; + // elem.style.cssText = oldStyle + ' content: "' + bodyText + parseInt(entry.responseEnd) + 'ms (' + parseInt(entry.duration) + 'ms)";'; + // } + // } + //document.body.appendChild(marker); } diff --git a/src/_vars.js b/src/_vars.js index 2b96332..36f36a0 100644 --- a/src/_vars.js +++ b/src/_vars.js @@ -5,20 +5,26 @@ var perfMap = {}, gZeroTop = 0, hArr = [{ threashold: 0.16, - value: '#1a9850' + value: '#1a9850', + rgba: 'rgba(26, 152, 80, 0.95)' }, { threashold: 0.32, - value: '#66bd63' + value: '#66bd63', + rgba: 'rgba(102, 189, 99, 0.95)' }, { threashold: 0.48, - value: '#a6d964' + value: '#a6d964', + rgba: 'rgba(166, 217, 100, 0.95)' }, { threashold: 0.64, - value: '#fdae61' + value: '#fdae61', + rgba: 'rgba(253, 174, 97, 0.95)' }, { threashold: 0.8, - value: '#f46d43' + value: '#f46d43', + rgba: 'rgba(244, 109, 67, 0.95)' }, { threashold: 1.1, - value: '#d73027' + value: '#d73027', + rgba: 'rgba(215, 48, 39, 0.95)' }]; \ No newline at end of file diff --git a/src/findImages.js b/src/findImages.js index b2c2183..ef6e474 100644 --- a/src/findImages.js +++ b/src/findImages.js @@ -14,6 +14,8 @@ function findImages() { for (var j = 0; j < len; j++) { el = getBgElement(tags[j]); if (!!el && !!el.src) { + el.bgImg = el.src; + var match = el.src.match(/\((.*?)\)/); if (match[1]) { el.src = match[1].replace(/('|")/g, ''); @@ -37,13 +39,13 @@ function findImages() { for (var i = 0; i < len; i++) { var entry = window.performance.getEntriesByName(imgs[i].src)[0]; if (entry) { - var xy = getCumulativeOffset(imgs[i].element, imgs[i].src); + //var xy = getCumulativeOffset(imgs[i].element, imgs[i].src); var wh = imgs[i].element.getBoundingClientRect(); var width = wh.width; var height = wh.height; if (width > 10) { if (height > 10) { - placeMarker(xy, width, height, entry, imgs[i].element.tagName === 'BODY', imgs[i].src); + placeMarker(width, height, entry, imgs[i].element.tagName === 'BODY', imgs[i].src, imgs[i]); } } } diff --git a/src/getBgImages.js b/src/getBgImages.js index 178f576..b3b2825 100644 --- a/src/getBgImages.js +++ b/src/getBgImages.js @@ -1,14 +1,16 @@ 'use strict'; + function getBgElement(el) { /*jshint sub: true */ //console.log(el.className); var style = el.currentStyle || window.getComputedStyle(el, false); //console.log(style.getPropertyValue('background-image')); - if (style.getPropertyValue('background-image') !== 'none') { + if (style.getPropertyValue('background-image') !== 'none') { return { - element: el, - src: style.getPropertyValue('background-image') - }; + element: el, + src: style.getPropertyValue('background-image'), + position: style.getPropertyValue('background-position') + }; } return null; } \ No newline at end of file diff --git a/src/getCumulativeOffset.js b/src/getCumulativeOffset.js index 2622add..9bb0ddb 100644 --- a/src/getCumulativeOffset.js +++ b/src/getCumulativeOffset.js @@ -1,6 +1,6 @@ 'use strict'; -function getCumulativeOffset(obj, url) { +/*function getCumulativeOffset(obj, url) { var left, top; left = top = 0; if (obj.offsetParent) { @@ -17,4 +17,4 @@ function getCumulativeOffset(obj, url) { left: left, top: top, }; -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/src/init.js b/src/init.js index 4458153..8fdca81 100644 --- a/src/init.js +++ b/src/init.js @@ -16,16 +16,20 @@ perfMap.init = function() { elements[i].onmouseover = function() { timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); if (this.dataset.body !=='1') { - this.style.opacity = 1; + //this.style.opacity = 1; + this.style.cssText = this.style.cssText.replace(/(\d\.\d*)\)/g, '0.1)'); + timeline.style.cssText = 'opacity:1; transition: 0.5s ease-in-out; transform: translate(' + parseInt(timelineLeft) + 'px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;'; } - timeline.style.cssText = 'opacity:1; transition: 0.5s ease-in-out; transform: translate(' + parseInt(timelineLeft) + 'px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;'; + }; elements[i].onmouseout = function() { timelineLeft = document.documentElement.clientWidth * (this.dataset.ms / loaded); if (this.dataset.body !== '1') { - this.style.opacity = 0.925; + //this.style.opacity = 0.925; + this.style.cssText = this.style.cssText.replace(/(\d\.\d*)\)/g, '0.95)'); + timeline.style.cssText = 'opacity:0; transition: 0.5s ease-in-out; transform: translate(' + parseInt(timelineLeft) + 'px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;'; } - timeline.style.cssText = 'opacity:0; transition: 0.5s ease-in-out; transform: translate(' + parseInt(timelineLeft) + 'px,0); position:absolute; z-index:4; border-left:2px solid white; height:100%;'; + }; } }; \ No newline at end of file diff --git a/src/placeMarker.js b/src/placeMarker.js index 244857c..723c293 100644 --- a/src/placeMarker.js +++ b/src/placeMarker.js @@ -1,6 +1,7 @@ 'use strict'; -function placeMarker(xy, width, height, entry, body, url) { +function placeMarker(width, height, entry, body, url, el) { + //background-image: linear-gradient(90deg, rgba(253, 174, 97, 0.95), rgba(253, 174, 97, 0.95)); var heat = (entry.responseEnd / loaded), marker = document.createElement('div'), padding = 9, @@ -33,15 +34,38 @@ function placeMarker(xy, width, height, entry, body, url) { paddingTop = 10; bodyText = 'BODY '; } + var elem = el.element; + //debugger; + var oldClass = elem.className; + elem.className = oldClass + ' perfmap'; + elem.setAttribute('data-ms', parseInt(entry.responseEnd)); + elem.setAttribute('data-body', (body ? 1 : 0)); + var oldStyle = elem.style.cssText; + var bgImg = ''; + if (!!el.bgImg) { + bgImg = ', ' + el.bgImg; + } else { + //debugger; + bgImg = ', url("' + elem.src + '") '; - marker.className = 'perfmap'; - marker.setAttribute('data-ms', parseInt(entry.responseEnd)); - marker.setAttribute('data-body', (body ? 1 : 0)); - marker.style.cssText = 'position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: ' + size + 'px; font-weight:800; font-family:"Helvetica Neue",sans-serif; text-align:' + align + '; opacity: ' + opacity + '; background: ' + heatmap(heat).value + '; top: ' + xy.top + 'px; left: ' + xy.left + 'px; width: ' + width + 'px; height:' + height + 'px; padding-top:' + paddingTop + 'px; z-index: 4000;'; - if (width > 50) { - if (height > 15) { - marker.innerHTML = bodyText + parseInt(entry.responseEnd) + 'ms (' + parseInt(entry.duration) + 'ms)'; - } + var style = elem.currentStyle || window.getComputedStyle(elem, false); + //console.log(style.getPropertyValue('background-image')); + oldStyle += 'width: ' + (style.getPropertyValue('width') || width + 'px') + '!important;'; + oldStyle += 'height: ' + ( style.getPropertyValue('height') || height + 'px') + '!important;'; + elem.removeAttribute("src"); } - document.body.appendChild(marker); + var bgPosition = 'background-position: 0px 0px'; + if (!!el.position) { + bgPosition += ', ' + el.position; + } + + elem.style.cssText = oldStyle + ' background-image: linear-gradient(' + heatmap(heat).rgba + ', ' + heatmap(heat).rgba + ')' + bgImg + '; ' + bgPosition + '; background-size: contain;'; + //elem.style.cssText = 'position:absolute; transition: 0.5s ease-in-out; box-sizing: border-box; color: #fff; padding-left:10px; padding-right:10px; line-height:14px; font-size: ' + size + 'px; font-weight:800; font-family:"Helvetica Neue",sans-serif; text-align:' + align + '; opacity: ' + opacity + '; background: ' + heatmap(heat).value + '; top: ' + xy.top + 'px; left: ' + xy.left + 'px; width: ' + width + 'px; height:' + height + 'px; padding-top:' + paddingTop + 'px; z-index: 4000;'; + // if (width > 50) { + // if (height > 15) { + // oldStyle = elem.style.cssText; + // elem.style.cssText = oldStyle + ' content: "' + bodyText + parseInt(entry.responseEnd) + 'ms (' + parseInt(entry.duration) + 'ms)";'; + // } + // } + //document.body.appendChild(marker); } \ No newline at end of file