function getEventX(event) { var posx = 0; if (event.pageX || event.pageY) { posx = event.pageX; } else if (event.clientX || event.clientY) { posx = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; } return posx; } function getElementX(obj) { var x = 0; if (obj.offsetParent) { do { x += obj.offsetLeft; } while (obj = obj.offsetParent); } return x; } function zeroPad(v, len) { v = v.toString(); return v.length >= len ? v : "00000000".substring(0, len - v.length) + v; } /** * Check if the difference between the max and the min non zero capture numbers * is larger than 3 orders of magnitude. If yes, we need to scale. **/ function capturegraph_scale_is_required(captures) { var max = 0; var min = 1000; for (var i = 0; i < captures.length; i++) { var year = captures[i]; max = Math.max(max, Math.max.apply(null, year[1])); min = Math.min(min, Math.min.apply(null, year[1].filter(Boolean))); } return (Math.log1p(max) - Math.log1p(min) > 3); } /** * Scale captugraph counts and max maxcount using log1p if necessary. */ function capturegraph_scale(captures) { var maxcount = 0; for (var i = 0; i < captures.length; i++) { maxcount = Math.max(maxcount, Math.max.apply(null, captures[i][1])); } if (capturegraph_scale_is_required(captures)) { var scaled = []; for (var i = 0; i < captures.length; i++) { var year = captures[i]; // XXX map may not be available on all platforms scaled.push([year[0], year[1].map(Math.log1p)]); } captures = scaled; maxcount = Math.log1p(maxcount); } return [captures, maxcount]; } /** * Draw years, highlight current year, draw capture frequency per month */ function sparkline(captures, width, height, canvas_id, start_year, cur_year, cur_month) { var c = document.getElementById(canvas_id); if (!c || !c.getContext) return; var ctx = c.getContext("2d"); if (!ctx) return; ctx.fillStyle = "#FFF"; var end_year = new Date().getUTCFullYear(); var year_width = width / (end_year - start_year + 1); var scaled = capturegraph_scale(captures.years); var years = scaled[0]; var maxcount = scaled[1]; var yscale = height / maxcount; function year_left(year) { return Math.ceil((year - start_year) * year_width) + 0.5; } if (cur_year >= start_year) { var x = year_left(cur_year); ctx.fillStyle = "#FFFFA5"; ctx.fillRect(x, 0, year_width, height); } for (var year = start_year; year <= end_year; year++) { var x = year_left(year); ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, height); ctx.lineWidth = 1; ctx.strokeStyle = "#CCC"; ctx.stroke(); } cur_month = parseInt(cur_month) - 1; var month_width = (year_width - 1) / 12; for (var i = 0; i < years.length; i++) { var year = years[i][0]; var months = years[i][1]; var left = year_left(year) + 1.0; for (var month = 0; month < 12; month++) { var count = months[month]; if (count > 0) { var h = Math.ceil(count * yscale); if (year == cur_year && month == cur_month) { ctx.fillStyle = "#EC008C"; } else { ctx.fillStyle = "#000"; } // must note that when I use width=Math.round(month_width+1), // the replay toolbar looks more accurate whereas the // bubble calendar looks somehow different. ctx.fillRect(Math.round(left), Math.ceil(height - h), Math.ceil(month_width), Math.round(h)); } left += month_width; } } } function clear_canvas(canvas_id) { var c = document.getElementById(canvas_id); if (!c || !c.getContext) return; var ctx = c.getContext("2d"); if (!ctx) return; ctx.clearRect(0, 0, c.width, c.height); }