Author: henkp
Date: Mon Jan 22 17:53:18 2018
New Revision: 1821911

URL: http://svn.apache.org/viewvc?rev=1821911&view=rev
Log:
+= index-hppdev.html js/render-hppdev.js

Added:
    comdev/reporter.apache.org/trunk/site/index-hppdev.html
    comdev/reporter.apache.org/trunk/site/js/render-hppdev.js

Added: comdev/reporter.apache.org/trunk/site/index-hppdev.html
URL: 
http://svn.apache.org/viewvc/comdev/reporter.apache.org/trunk/site/index-hppdev.html?rev=1821911&view=auto
==============================================================================
--- comdev/reporter.apache.org/trunk/site/index-hppdev.html (added)
+++ comdev/reporter.apache.org/trunk/site/index-hppdev.html Mon Jan 22 17:53:18 
2018
@@ -0,0 +1,149 @@
+<!doctype html>
+<html class="no-js" lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <link rel="stylesheet" href="css/foundation.css" />    
+    <script src="js/vendor/modernizr.js"></script>
+   <script src="https://code.jquery.com/jquery-latest.min.js"; 
type="text/javascript"></script>
+   <script src="https://code.jquery.com/ui/1.11.3/jquery-ui.js"; 
type="text/javascript"></script>
+   <link rel="stylesheet" 
href="//code.jquery.com/ui/1.11.3/themes/smoothness/jquery-ui.css">
+   <script type="text/javascript" src="https://www.google.com/jsapi";></script>
+   <script src="js/render-hppdev.js"></script>
+   <style type="text/css">
+    html,body {
+      background: #DDD;
+      height:100%;
+      padding:0;
+      margin:0;
+  }
+    #tabs {
+      overflow: hidden;
+      width: 100%;
+      margin: 0;
+      padding: 0;
+      list-style: none;
+    }
+    
+    #tabs li {
+      float: left;
+      margin: 0 .5em 0 0;
+    }
+    
+    #tabs a {
+      position: relative;
+      background: #ddd;
+      background-image:  linear-gradient(to bottom, #fceabb 0%,#fccd4d 
50%,#f8b500 51%,#fbdf93 100%);
+      padding: .7em 1.5em;
+      float: left;
+      text-decoration: none;
+      color: #444;
+      text-shadow: 0 1px 0 rgba(255,255,255,.8);
+      border-radius: 5px 0 0 0;
+      box-shadow: 0 2px 2px rgba(0,0,0,.4);
+    }
+    
+    #tabs a:hover,
+    #tabs a:hover::after,
+    #tabs a:focus,
+    #tabs a:focus::after {
+      background: linear-gradient(to bottom, #b7deed 0%,#71ceef 50%,#21b4e2 
51%,#b7deed 100%);
+    }
+    
+    #tabs a:focus {
+      outline: 0;
+    }
+    
+    #tabs a::after {
+      content:'';
+      position:absolute;
+      z-index: 1;
+      top: 0;
+      right: -.5em;  
+      bottom: 0;
+      width: 1em;
+      background: #ddd;
+      background-image:  linear-gradient(to bottom, #fceabb 0%,#fccd4d 
50%,#f8b500 51%,#fbdf93 100%);
+      box-shadow: 2px 2px 2px rgba(0,0,0,.4);
+      transform: skew(10deg);
+      border-radius: 0 5px 0 0;  
+    }
+    
+    #tabs #current a,
+    #tabs #current a::after, #tabs #current a::before {
+      background: linear-gradient(to bottom, #b7deed 0%,#71ceef 50%,#21b4e2 
51%,#b7deed 100%);
+      z-index: 3;
+    }
+    #contents {
+      height: calc(100% - 200px)
+    }
+    
+   
+    #tabcontents {
+      background: #fff;
+      padding: 2em;
+      height: 220px;
+      position: relative;
+      z-index: 2; 
+      border-radius: 0 5px 5px 5px;
+      box-shadow: 0 -2px 3px -2px rgba(0, 0, 0, .5);
+    }
+    
+    #footer {
+      break-before: always;
+      position:absolute;
+      bottom:20px;
+      font-style: italic;
+      font-size: small;
+      text-align: center;
+      width: 100%;
+    }
+    
+    @media only screen and (max-height: 680px) {
+      #footer {
+        display: none;
+      }
+      
+      h2 {
+        font-size: 18px;
+        font-weight: bold;
+        margin: 5px;
+      }
+      #contents {
+        height: calc(100% - 120px);
+      }
+    }
+    
+   </style>
+   <title>Apache Committee Report Helper</title>
+   <meta name="description" content="Tool to draft sample quarterly board 
reports for Apache PMCs">
+</head>
+<body>
+
+<!-- width etc. must agree with render.js/pcontainer.setAttribute -->
+<div id="contents" class="row-12" style="text-align: left; margin: 0 auto; 
width: 1200px; ">
+   <p style="text-align: center;">
+      <div id="pct">Apache Project Quarterly Report Helper<br/>Requesting data 
for projects you have access to, please wait...</div>
+      <div id="chart"></div>
+   </p>
+   <noscript>
+      This site relies heavily on JavaScript.
+      Please enable it or get a browser that supports it.
+   </noscript>
+</div>
+<div id="footer" class="footer row">
+  <p>&nbsp;</p>
+  <p>
+    Managed by the <a href="http://community.apache.org/";>Apache Community 
Development Project</a>, see <a 
href="https://reporter.apache.org/about.html";>About This Website</a> for 
technical info.<br/>
+    Copyright&copy; 2017, the Apache Software Foundation. Licensed under the 
<a rel="license" href="http://www.apache.org/licenses/LICENSE-2.0";>Apache 
License, Version 2.0</a><br/>
+  </p>
+</div>
+<script type="text/javascript">
+   google.load("visualization", "1", {packages:["corechart", "timeline"]});
+   google.setOnLoadCallback(function() {
+      var project = document.location.search.substr(1);
+      GetAsyncJSON("getjson.py?" + project, project, renderFrontPage)
+   });
+</script>
+</body>
+</html>

Added: comdev/reporter.apache.org/trunk/site/js/render-hppdev.js
URL: 
http://svn.apache.org/viewvc/comdev/reporter.apache.org/trunk/site/js/render-hppdev.js?rev=1821911&view=auto
==============================================================================
--- comdev/reporter.apache.org/trunk/site/js/render-hppdev.js (added)
+++ comdev/reporter.apache.org/trunk/site/js/render-hppdev.js Mon Jan 22 
17:53:18 2018
@@ -0,0 +1,1058 @@
+var jsdata = {}
+
+var templates = {}
+var nproject = null;
+var animals = ['hedgehogs', 'cows', 'geese', 'pigs', 'fluffy kittens', 
'puppies', 'rabid dogs', 'ponies', 'weevils']
+
+// This is faster than parseInt, and it's more obvious what is being done
+function toInt(number) {
+    return number | 0 //
+}
+
+// Function for async fetching of a single JSON file with JS callback
+// Parses Url as JSON and calls callback(JSON, xstate)
+
+function GetAsyncJSON(theUrl, xstate, callback) {
+       var xmlHttp = null;
+       if (window.XMLHttpRequest) {
+               xmlHttp = new XMLHttpRequest();
+       } else {
+           xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
+       }
+       xmlHttp.open("GET", theUrl, true);
+       xmlHttp.send(null);
+       xmlHttp.onprogress = function(state) {
+               var s = parseInt(xmlHttp.getResponseHeader('Content-Length'))
+               if (document.getElementById('pct')) {
+                       document.getElementById('pct').innerHTML = "<p 
style='text-align: center;'><b><i>Loading: " + parseInt((100 * 
(xmlHttp.responseText.length / s))) + "% done</i></b></p>";
+               }
+       }
+       xmlHttp.onreadystatechange = function(state) {
+
+               if (xmlHttp.readyState == 4 && xmlHttp.status == 200 || 
xmlHttp.status == 404) {
+                       if (callback) {
+                               if (xmlHttp.status == 404) {
+                                       callback({}, xstate);
+                               } else {
+                                       if (document.getElementById('pct')) {
+                                               
document.getElementById('pct').innerHTML = "<p style='text-align: 
center;'><b><i>Loading: 100% done</i></b></p>";
+                                       }
+                                       window.setTimeout(callback, 0.05, 
JSON.parse(xmlHttp.responseText), xstate);
+                               }
+                       }
+               }
+       }
+}
+
+
+function makeSelect(name, arr) {
+       var sel = document.createElement('select');
+       sel.setAttribute("name", name)
+       for (i in arr) {
+               var val = arr[i];
+               var opt = document.createElement('option')
+               opt.setAttribute("value", val)
+               opt.innerHTML = val
+               sel.appendChild(opt);
+       }
+       return sel
+}
+
+function getWednesdays(mo, y) {
+       var d = new Date();
+       if (mo) {
+               d.setMonth(mo);
+       }
+       if (y) {
+               d.setFullYear(y, d.getMonth(), d.getDate())
+       }
+       var month = d.getMonth(),
+               wednesdays = [];
+
+       d.setDate(1);
+
+       // Get the first Wednesday (day 3 of week) in the month
+       while (d.getDay() !== 3) {
+               d.setDate(d.getDate() + 1);
+       }
+
+       // Get all the other Wednesdays in the month
+       while (d.getMonth() === month) {
+               wednesdays.push(new Date(d.getTime()));
+               d.setDate(d.getDate() + 7);
+       }
+
+       return wednesdays;
+}
+// check if the entry is a wildcard month
+
+function everyMonth(s) {
+       if (s.indexOf('Next month') == 0) {
+               return true
+       }
+       return s == 'Every month'
+}
+
+var m = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 
'August', 'September', 'October', 'November', 'December']
+
+// Format the report month array. Assumes that non-month values appear first
+
+function formatRm(array) {
+    var first = array[0]
+    if (array.length == 1) { // e.g. every month
+        return first
+    }
+    if (m.indexOf(first) < 0) { // non-month value initially
+        return  first.concat('; (default: ', array.slice(1).join(', '),')')
+    }
+    return array.join(', ')
+}
+
+// Called by: GetAsyncJSON("reportingcycles.json?" + Math.random(), [pmc, 
reportdate, json.pdata[pmc].name], setReportDate) 
+
+function setReportDate(json, x) {
+       var pmc = x[0]
+       var reportdate = x[1]
+       var fullname = (x[2] ? x[2] : "Unknown").replace(/Apache /, "")
+       var today = new Date()
+
+       var dates = [] // the entries must be in date order
+       if (!json[pmc]) {
+               pmc = fullname
+       }
+
+       var rm = json[pmc] // reporting months for the pmc
+
+       // First check if the list contains an every month indicator
+       // This is necessary to ensure that the dates are added to the list in 
order
+       for (var i in json[pmc]) {
+               var sm = json[pmc][i]
+               if (everyMonth(sm)) {
+                       rm = m // reset to every month
+                       break
+               }
+       }
+
+       // Check the months in order, so it does not matter if the data is 
unordered
+       for (var x in m) {
+               for (i in rm) {
+                       if (m[x] == rm[i]) {
+                               dates.push(getWednesdays(x)[2])
+                       }
+               }
+       }
+       // cannot combine with the code above because that would destroy the 
order
+       var ny = today.getFullYear() + 1;
+       for (x in m) {
+               for (i in rm) {
+                       if (m[x] == rm[i]) {
+                               dates.push(getWednesdays(x, ny)[2])
+                       }
+               }
+       }
+       var nextdate = dates[0];
+       while (nextdate < today) {
+               nextdate = dates.shift();
+       }
+       reportdate.innerHTML += "<b>Reporting schedule:</b> " + (json[pmc] ? 
formatRm(json[pmc]) : "Unknown(?)") + "<br>"
+       reportdate.innerHTML += "<b>Next report date: " + (nextdate ? 
nextdate.toDateString() : "Unknown(?)") + "</b>"
+       if (nextdate) {
+               var link = 
"https://svn.apache.org/repos/private/foundation/board/board_agenda_"; + 
nextdate.getFullYear() +
+                       "_" + (nextdate.getMonth() < 9 ? "0" : "") + 
(nextdate.getMonth() + 1) + "_" + nextdate.getDate() + ".txt"
+               reportdate.innerHTML += "<br>File your report in <a href='" + 
link + "'>" + link + "</a> when it has been seeded."
+       }
+
+}
+
+function buildPanel(pmc, title) {
+       var parent = document.getElementById('tab_' + pmc);
+
+       var toc = document.getElementById('toc_' + pmc);
+       if (!toc) {
+               toc = document.createElement('cl')
+               toc.setAttribute("class", "sub-nav")
+               toc.setAttribute("id", "toc_" + pmc)
+               if (parent.firstChild.nextSibling) {
+                       parent.insertBefore(toc, parent.firstChild.nextSibling);
+               } else {
+                       parent.appendChild(toc)
+               }
+       }
+       var linkname = title.toLowerCase().replace(/[^a-z0-9]+/, "")
+       var li = document.createElement('dd');
+       li.setAttribute("role", "menu-item")
+       li.innerHTML = "<a href='#" + linkname + "_" + pmc + "'>" + title + 
"</a>"
+       toc.appendChild(li)
+
+       var div = document.createElement('div');
+       div.setAttribute("id", linkname + "_" + pmc);
+       parent.appendChild(div)
+
+       var titlebox = document.createElement('div');
+       titlebox.innerHTML = "<h3 style='background: #666; color: #EEE; border: 
1px solid #66A; margin-top: 30px;'>" + title + " &nbsp; &nbsp; <small> 
<b>&uarr;</b> <a href='#tab_" + pmc + "'>Back to top</a></small></h3>"
+       div.appendChild(titlebox);
+       return div;
+}
+
+function addLine(pmc, line) {
+       line = line ? line : "  "
+       var lines = line.split(/\n/)
+       for (x in lines) {
+               var xline = lines[x]
+               var words = xline.split(" ")
+               var len = 0;
+               var out = ""
+               for (i in words) {
+                       len += words[i].replace(/<.+?>/, "").length + (i == 
words.length - 1 ? 0 : 1)
+                       if (len >= 78) {
+                               out += "\n   "
+                               len = words[i].replace(/<.+?>/, "").length + (i 
== words.length - 1 ? 0 : 1)
+                       }
+                       out += words[i] + " "
+               }
+               templates[pmc] += out + "\n"
+       }
+}
+
+function isNewPMC(json,pmc,after) {
+    return json.pmcdates[pmc].pmc[1] >= (after.getTime() / 1000)
+}
+
+function PMCchanges(json, pmc, after) {
+        var changes = buildPanel(pmc, "PMC changes (from committee-info)");
+
+        var roster = json.pmcdates[pmc].roster
+        var nc = 0; // newest committer start date
+        var np = 0; // newest pmc member start date
+        var ncn = null; // newest committer name
+        var npn = null; // newest pmc name
+        var afterTime = after.getTime() / 1000
+        var pmcStartTime = json.pmcdates[pmc].pmc[2]
+
+        addLine(pmc, "## PMC changes:")
+        addLine(pmc)
+        if (pmcStartTime > afterTime) {
+            afterTime = pmcStartTime
+            changes.innerHTML += "<h5>Changes since PMC creation:</h5>"
+        } else {
+            changes.innerHTML += "<h5>Changes within the last 3 months:</h5>"
+        }
+
+        // pre-flight check
+        var c = 0; // total number of pmc members
+        var npmc = 0; // number of recent pmc members
+        for (i in roster) {
+            c++
+            var entry = roster[i];
+            if (entry[1] > afterTime) {
+                npmc++;
+            }
+        }
+        addLine(pmc, " - Currently " + c + " PMC members.")
+        if (npmc > 1) {
+            addLine(pmc, " - New PMC members:")
+        }
+
+        for (i in roster) {
+            var entry = roster[i];
+            if (entry[1] > np) { // find most recent member
+                np = entry[1]    // start date
+                npn = entry[0];  // full name
+            }
+            if (entry[1] > afterTime) {
+                changes.innerHTML += "&rarr; " + entry[0] + " was added to the 
PMC on " + new Date(entry[1] * 1000).toDateString() + "<br>";
+                addLine(pmc, (npmc > 1 ? "   " : "") + " - " + entry[0] + " 
was added to the PMC on " + new Date(entry[1] * 1000).toDateString())
+            }
+        }
+        if (npmc == 0) {
+            addLine(pmc, " - No new PMC members added in the last 3 months")
+            changes.innerHTML += "&rarr; <font color='red'><b>No new PMC 
members in the last 3 months.</b></font><br>";
+        }
+        if (npn) {
+            if (np < afterTime) {
+                addLine(pmc, " - Last PMC addition was " + npn + " on " + new 
Date(np * 1000).toDateString())
+            }
+            changes.innerHTML += "&rarr; " + "<b>Last PMC addition: </b>" + 
new Date(np * 1000).toDateString() + " (" + npn + ")<br>"
+        }
+        changes.innerHTML += "&rarr; " + "<b>Currently " + c + " PMC 
members.<br>"
+        changes.innerHTML += "<br>PMC established: " + 
json.pmcdates[pmc].pmc[0]
+        if (pmcStartTime > 0) { // don't use missing time
+            changes.innerHTML += " (assumed actual date: " + 
epochSecsYYYYMMDD(pmcStartTime) + ")"
+        }
+        addLine(pmc)
+}
+
+function epochSecsYYYYMMDD(t) {
+    return new Date(t * 1000).toISOString().slice(0, 10)
+}
+
+function renderFrontPage(json) {
+    var thisHour = toInt(new Date().getTime() / (3600*1000)) // this changes 
once per hour
+       jsdata = json
+       var container = document.getElementById('contents')
+       container.innerHTML = "<h2 style='text-align: center; margin-bottom: 
10px;' class='hide-for-small-only'>Apache Committee Report Helper</h2>Click on 
a committee name to view statistics:"
+       var top = document.createElement('div');
+       container.appendChild(top)
+
+
+       var panellist = document.createElement('ul');
+       panellist.style.background = "#AAA"
+       panellist.style.textAlign = "center"
+       panellist.style.margin = "0 auto"
+       panellist.style.paddingLeft = "5px"
+       //panellist.setAttribute("class", "tabs")
+       panellist.setAttribute("id", "tabs");
+       panellist.setAttribute("data-tab", "")
+       panellist.setAttribute("role", "tablist")
+       container.appendChild(panellist)
+
+       var pcontainer = document.createElement('div');
+       pcontainer.setAttribute("id", "tabcontents")
+       // width etc must agree with index.html
+       pcontainer.setAttribute("style", "text-align: left !important; margin: 
0 auto; width: 1200px; border-radius: 5px; border: 2px solid #666; height: 
100%; overflow: scroll !important; overflow-y: scroll !important; ")
+       container.appendChild(pcontainer)
+
+       var sproject = document.location.search.substr(1);
+       var hcolors = ["#000070", "#007000", "#407000", "#70500", "#700000", 
"#A00000"]
+       var hvalues = ["Super Healthy", "Healthy", "Mostly Okay", "Unhealthy", 
"Action required!", "URGENT ACTION REQUIRED!"]
+       for (i in json.pmcs) {
+               var pmc = json.pmcs[i]
+               
+               // Stuff has broken, check that we have dates!
+               if (!json.pmcdates[pmc]) {
+                       continue
+               }
+               templates[pmc] = ""
+
+               addLine(pmc, "## Description:")
+               if (json.pdata[pmc].shortdesc) {
+                       addLine(pmc, "   " + json.pdata[pmc].shortdesc)
+               } else {
+                       addLine(pmc, " - <font color='red'>Description goes 
here</font>")
+               }
+               addLine(pmc)
+
+               var a = animals[Math.floor(Math.random()*animals.length*0.999)]
+               addLine(pmc, "## Issues:")
+               addLine(pmc, " - <font color='red'>TODO - list any issues that 
require board attention, \n  or say \"there are no issues requiring board 
attention at this time\" - if not, the " + a + " will get you.</font>")
+               addLine(pmc)
+
+               addLine(pmc, "## Activity:")
+               addLine(pmc, " - <font color='red'>TODO - the PMC 
<b><u>MUST</u></b> provide this information</font>")
+               addLine(pmc)
+
+               
+               a = animals[Math.floor(Math.random()*animals.length*0.999)]
+               addLine(pmc, "## Health report:")
+               addLine(pmc, " - <font color='red'>TODO - Please use this 
paragraph to elaborate on why the current project activity (mails, commits, 
bugs etc) is at its current level - Maybe " + a + " took over and are now 
controlling the project?</font>")
+               addLine(pmc)
+
+               var obj = document.createElement('div');
+               obj.setAttribute("id", "tab_" + pmc)
+               obj.style = "padding: 10px; text-align: left !important;"
+               obj.setAttribute("aria-hidden", "true")
+               var title = document.createElement('h2')
+               title.innerHTML = json.pdata[pmc].name ? json.pdata[pmc].name : 
pmc
+               obj.appendChild(title)
+               var health = document.createElement('p');
+               if (json.health[pmc] && !isNaN(json.health[pmc]['cscore'])) {
+                       health.style.marginTop = "10px"
+                       health.innerHTML = "<b>Committee Health score:</b> <a 
href='chi.py#" + pmc + "'><u><font color='" + 
hcolors[json.health[pmc]['cscore']] + "'>" + (6.33 + (json.health[pmc]['score'] 
* -1.00 * (20 / 12.25))).toFixed(2) + " (" + 
hvalues[json.health[pmc]['cscore']] + ")</u></font></a> <i>(This is an 
automatically generated score, it is NOT authoritative in any way!)</i>"
+                       obj.appendChild(health)
+               }
+               pcontainer.appendChild(obj)
+
+
+
+               // Report date
+
+               var reportdate = buildPanel(pmc, "Report date")
+               if (json.pdata[pmc].chair) {
+                       reportdate.innerHTML += "<b>Committee Chair: </b>" + 
json.pdata[pmc].chair + "<br>"
+               }
+
+               GetAsyncJSON("reportingcycles.json?" + thisHour, [pmc, 
reportdate, json.pdata[pmc].name], setReportDate)
+
+
+               // LDAP committee + Committer changes
+
+               var mo = new Date().getMonth() - 3;
+               var after = new Date();
+               after.setMonth(mo); // This also works if mo is negative
+
+        PMCchanges(json, pmc, after)
+
+               var changes = buildPanel(pmc, "PMC changes (From LDAP)");
+
+               var c = 0; // total number of committer + pmc changes since 
establishment
+               var cu = 0; // total number of committer (user) changes
+               for (i in json.changes[pmc].committer) {cu++; c++;}
+               for (i in json.changes[pmc].pmc) c++;
+               var nc = 0; // newest committer date
+               var np = 0; // newest pmc date
+               var ncn = null; // newest committer name
+               var npn = null; // newest pmc name
+
+               addLine(pmc, "## Committer base changes:")
+               addLine(pmc)
+               addLine(pmc, " - Currently " + json.count[pmc][1] + " 
committers.")
+               if (cu == 0) { // no new committers
+            if (isNewPMC(json,pmc,after)) {
+                addLine(pmc, " - No changes (the PMC was established in the 
last 3 months)")
+            } else {
+                addLine(pmc, " - No new changes to the committer base since 
last report.")
+            }
+            addLine(pmc)
+               }
+               if (c == 0) { // no changes at all
+                   if (isNewPMC(json,pmc,after)) {
+                changes.innerHTML += "No changes - the PMC was established in 
the last 3 months."
+                   } else {
+                           changes.innerHTML += "<font color='red'><b>No new 
changes to the PMC or committer base detected - (LDAP error or no changes for 
&gt;2 years)</b></font>"
+                       }
+               } else {
+                       changes.innerHTML += "<h5>Changes within the last 3 
months:</h5>"
+
+                       // pre-flight check
+                       var npmc = 0; // recent committee group additions
+                       for (i in json.changes[pmc].pmc) {
+                               var entry = json.changes[pmc].pmc[i];
+                               if (entry[1] > after.getTime() / 1000) {
+                                       npmc++;
+                               }
+                       }
+
+                       for (i in json.changes[pmc].pmc) {
+                               var entry = json.changes[pmc].pmc[i];
+                               if (entry[1] > np) { // latest pmc member date
+                                       np = entry[1]
+                                       npn = entry[0]; // latest pmc member 
name
+                               }
+                               if (entry[1] > after.getTime() / 1000) {
+                                       changes.innerHTML += "&rarr; " + 
entry[0] + " was added to the PMC on " + new Date(entry[1] * 
1000).toDateString() + "<br>";
+                               }
+                       }
+                       if (npmc == 0) { // PMC older than 3 months itself
+                           if (isNewPMC(json,pmc,after)) {
+                    changes.innerHTML += "&rarr; No new PMC members in the 3 
months since the PMC was established<br>";
+                           } else {
+                                   changes.innerHTML += "&rarr; <font 
color='red'><b>No new PMC members in the last 3 months.</b></font><br>";
+                               }
+                       }
+                       if (npn) {
+                               changes.innerHTML += "&rarr; " + "<b>Last PMC 
addition: </b>" + new Date(np * 1000).toDateString() + " (" + npn + ")<br>"
+                       }
+
+
+                       // pre-flight check
+                       var ncom = 0; // number of new committers
+                       for (i in json.changes[pmc].committer) {
+                               var entry = json.changes[pmc].committer[i];
+                               if (entry[1] > after.getTime() / 1000) { // 
entry[1] is the first seen timestamp
+                                       ncom++;
+                               }
+                       }
+                       if (ncom > 1) {
+                               addLine(pmc, " - New commmitters:")
+                       }
+                       for (i in json.changes[pmc].committer) {
+                               var entry = json.changes[pmc].committer[i];
+                               if (entry[1] > nc) { // find the most recent 
entry
+                                       nc = entry[1]    // the timestamp
+                                       ncn = entry[0];  // full name
+                               }
+                               if (entry[1] > after.getTime() / 1000) {
+                                       changes.innerHTML += "&rarr; " + 
entry[0] + " was added as a committer on " + new Date(entry[1] * 
1000).toDateString() + "<br>";
+                                       addLine(pmc, (ncom > 1 ? "   " : "") + 
" - " + entry[0] + " was added as a committer on " + new Date(entry[1] * 
1000).toDateString())
+                               }
+                       }
+                       if (ncom == 0) {
+                               changes.innerHTML += "&rarr; <font 
color='red'><b>No new committers in the last 3 months.</b></font><br>";
+                               addLine(pmc, " - No new committers added in the 
last 3 months")
+                       }
+
+                       if (ncn) {
+                               if (nc < after.getTime() / 1000) {
+                                       addLine(pmc, " - Last committer 
addition was " + ncn + " at " + new Date(nc * 1000).toDateString())
+                               }
+                               changes.innerHTML += "&rarr; " + "<b>Last 
committer addition: </b>" + new Date(nc * 1000).toDateString() + " (" + ncn + 
")<br>"
+                       } else {
+                               addLine(pmc, " - Last committer addition was 
more than 2 years ago")
+                               changes.innerHTML += "&rarr; " + "<b>Last 
committer addition: </b><font color='red'>more than two years ago (not in the 
archive!)</font><br>"
+                       }
+                       changes.innerHTML += "&rarr; " + "<b>Currently " + 
json.count[pmc][1] + " committers and " + json.count[pmc][0] + " PMC members."
+                       addLine(pmc)
+               }
+
+               // Release data
+
+               var releases = buildPanel(pmc, "Releases")
+               addLine(pmc, "## Releases:")
+               addLine(pmc)
+               var nr = 0;
+               var lr = null;
+               var lrn = 0;
+               var tr = 0
+               for (version in json.releases[pmc]) {
+                       tr++;
+                       var date = parseInt(json.releases[pmc][version])
+                       if (date > lrn) {
+                               lrn = date
+                               lr = version
+                       }
+                       if (date >= after.getTime() / 1000) {
+                               var err = ""
+                               if (new Date(date * 1000) > new Date()) {
+                                       err = " (<font color='red'>This seems 
wrong?!</font>)"
+                               }
+                               releases.innerHTML += "&rarr; " + "<b>" + 
version + "</b> was released on " + new Date(date * 1000).toDateString() + err 
+ "<br>"
+                               addLine(pmc, " - " + version + " was released 
on " + new Date(date * 1000).toDateString() + err)
+                               nr++;
+                       }
+               }
+
+               if (nr == 0) {
+                       if (lr) {
+                               releases.innerHTML += "&rarr; " + "<b>Last 
release was " + lr + ", released on </b>" + new Date(lrn * 1000).toDateString() 
+ "<br>"
+                               addLine(pmc, " - Last release was " + lr + " on 
" + new Date(lrn * 1000).toDateString())
+                               if (lr.match("incubat") && 
!isNewPMC(json,pmc,after)) {
+                                   releases.innerHTML += "<br><font 
color='red'><b>No release since graduation</b></font><br><br>"
+                                   addLine(pmc, " - <font color='red'><b>No 
release since graduation??? [FIX!]</b></font>")
+                               }
+                       } else {
+                               releases.innerHTML += "No release data could be 
found.<br>"
+                               addLine(pmc, " - <font color='red'>No release 
data could be found [FIX!]</font>")
+                       }
+               }
+               releases.innerHTML += "<i>(A total of " + (tr - nr) + " older 
release(s) were found for " + pmc + " in our db)</i><br>"
+               releases.innerHTML += "<br><a href='javascript:void(0);' 
onclick=\"$('#rdialog_" + pmc + "').dialog({minWidth: 450, minHeight: 
240});\">Add a release</a>"
+               releases.innerHTML += " - <a href='javascript:void(0);' 
onclick=\"$('#dialog_" + pmc + "').dialog({minWidth: 450, minHeight: 
240});\">Fetch releases from JIRA</a>"
+               releases.innerHTML += " - <a href='addrelease.html?" + pmc + 
"'>Manage release versions</a><br>"
+
+               if (tr > 0) {
+                       var div = renderReleaseChart(json.releases[pmc], pmc, 
releases);
+                       releases.appendChild(div)
+               }
+
+
+               addLine(pmc)
+
+               var mlbox = buildPanel(pmc, "Mailing lists");
+
+               var ul = document.createElement('ul')
+               ul.style.textAlign = "left;"
+               mlbox.appendChild(ul)
+               var prev = ""
+               var f = 0
+               addLine(pmc, "## Mailing list activity:")
+               addLine(pmc)
+               addLine(pmc, " - <font color='red'>TODO Please explain what the 
following statistics mean for the project." +
+                               " If there is nothing significant in the 
figures, omit this section.</font>")
+               addLine(pmc)
+
+               var first = ['users', 'dev', 'commits', 'private', 'bugs', 
'modules-dev'];
+
+
+               for (i in first) {
+
+                       var ml = pmc + ".apache.org-" + first[i]
+                       if (ml != prev && ml.search("infra") < 0 && 
json.mail[pmc] && json.mail[pmc][ml]) {
+                               f++;
+                               prev = ml
+                               var d = ml.split(".org-");
+                               var mlname = d[1] + "@" + d[0] + ".org"
+                               var lookup = d[0].split(/\./)[0] + "-" + d[1]
+
+                               var x = renderChart(json.mail[pmc], ml, obj, 
(json.delivery[pmc] && json.delivery[pmc][lookup]) ? 
json.delivery[pmc][lookup].weekly : {})
+                               var total = x[0]
+                               var diff = x[1]
+                               var div = x[2]
+
+                               var add = ""
+                               if (json.delivery[pmc] && 
json.delivery[pmc][lookup]) {
+                                       add = ":\n    - " + 
json.delivery[pmc][lookup].quarterly[0] + " emails sent to list (" + 
json.delivery[pmc][lookup].quarterly[1] + " in previous quarter)";
+                               }
+                               var text = "Currently: " + total + " 
subscribers <font color='green'>(up " + diff + " in the last 3 months)</font>"
+                               if (diff < 0) {
+                                       text = "Currently: " + total + " 
subscribers <font color='red'>(down " + diff + " in the last 3 months)</font>"
+                                       if (d[1] != "private" && d[1] != 
"security" && d[1] != "commits") {
+                                               addLine(pmc, " - " + mlname + 
": ")
+                                               addLine(pmc, "    - " + total + 
" subscribers (down " + diff + " in the last 3 months)" + add)
+                                               addLine(pmc)
+                                       }
+                               } else {
+                                       if (d[1] != "private" && d[1] != 
"security" && d[1] != "commits") {
+                                               addLine(pmc, " - " + mlname + 
": ")
+                                               addLine(pmc, "    - " + total + 
" subscribers (up " + diff + " in the last 3 months)" + add)
+                                               addLine(pmc)
+                                       }
+                               }
+
+                               if (json.delivery[pmc] && 
json.delivery[pmc][lookup]) {
+                                       text += " (" + 
json.delivery[pmc][lookup].quarterly[0] + " emails sent in the past 3 months, " 
+ json.delivery[pmc][lookup].quarterly[1] + " in the previous cycle)"
+                               }
+
+                               var p = document.createElement('li');
+                               p.innerHTML = "<h5>" + mlname + ":</h5>" + text
+                               p.appendChild(div)
+                               ul.appendChild(p)
+                       }
+               }
+
+               for (ml in json.mail[pmc]) {
+                       var skip = false
+                       for (i in first) {
+                               var xml = pmc + ".apache.org-" + first[i]
+                               if (ml.search(xml) == 0) {
+                                       skip = true
+                               }
+                       }
+                       if (!skip) {
+
+                               f++;
+                               if (ml != prev && ml.search("infra") < 0) {
+                                       prev = ml
+                                       var d = ml.split(".org-");
+                                       var mlname = d[1] + "@" + d[0] + ".org"
+                                       var lookup = d[0].split(/\./)[0] + "-" 
+ d[1]
+                                       var x = renderChart(json.mail[pmc], ml, 
obj, (json.delivery[pmc] && json.delivery[pmc][lookup]) ? 
json.delivery[pmc][lookup].weekly : {})
+                                       var total = x[0]
+                                       var diff = x[1]
+                                       var div = x[2]
+
+                                       add = ""
+                                       if (json.delivery[pmc] && 
json.delivery[pmc][lookup]) {
+                                               add = ":\n    - " + 
json.delivery[pmc][lookup].quarterly[0] + " emails sent to list (" + 
json.delivery[pmc][lookup].quarterly[1] + " in previous quarter)";
+                                       }
+                                       var text = "Currently: " + total + " 
subscribers <font color='green'>(up " + diff + " in the last 3 months)</font>"
+                                       if (diff < 0) {
+                                               text = "Currently: " + total + 
" subscribers <font color='red'>(down " + diff + " in the last 3 months)</font>"
+                                               if (d[1] != "private" && d[1] 
!= "security" && d[1] != "commits") {
+                                                       addLine(pmc, " - " + 
mlname + ": ")
+                                                       addLine(pmc, "    - " + 
total + " subscribers (down " + diff + " in the last 3 months)" + add)
+                                                       addLine(pmc)
+                                               }
+                                       } else {
+                                               if (d[1] != "private" && d[1] 
!= "security" && d[1] != "commits") {
+                                                       addLine(pmc, " - " + 
mlname + ": ")
+                                                       addLine(pmc, "    - " + 
total + " subscribers (up " + diff + " in the last 3 months)" + add)
+                                                       addLine(pmc)
+                                               }
+                                       }
+
+                                       if (json.delivery[pmc] && 
json.delivery[pmc][lookup]) {
+                                               text += " (" + 
json.delivery[pmc][lookup].quarterly[0] + " emails sent in the past 3 months, " 
+ json.delivery[pmc][lookup].quarterly[1] + " in the previous cycle)"
+                                       }
+
+                                       var p = document.createElement('li');
+                                       p.innerHTML = "<h5>" + mlname + 
":</h5>" + text
+                                       p.appendChild(div)
+                                       ul.appendChild(p)
+                               }
+                       }
+               }
+               addLine(pmc)
+
+               // Add btn for nav
+               if (f > 0) {
+                       var btn = document.createElement('li');
+                       btn.setAttribute("id", "btn_" + pmc)
+                       btn.setAttribute("class", "tab-title")
+                       btn.setAttribute("onclick", 
"$('#tabcontents').animate({scrollTop: -99999}, 500)");
+                       btn.innerHTML = "<a href='#' name='tab_" + pmc + "'>" + 
pmc + "</a>"
+                       panellist.appendChild(btn)
+                       if (sproject && sproject == pmc) {
+                               $('#btn_' + pmc).click();
+                               $('#' + pmc).addClass("active");
+                       }
+
+               }
+
+
+
+
+        if (json.bugzilla[pmc][0] || json.bugzilla[pmc][1] > 0) {
+            renderBZ(pmc)
+        }
+
+               if (json.jira[pmc][0] > 0 || json.jira[pmc][1] > 0) {
+                       renderJIRA(pmc)
+               }
+
+
+               // Reporting example
+               var template = buildPanel(pmc, "Report template");
+               template.innerHTML += "<pre style='border: 2px dotted #444; 
padding: 10px; background: #FFD;' contenteditable='true'>" + templates[pmc] + 
"</pre>"
+
+               // Fetch from JIRA dialog
+               var dialog = document.createElement('div');
+               dialog.setAttribute("id", "dialog_" + pmc);
+               dialog.setAttribute("title", "Fetch data from JIRA for " + pmc)
+               dialog.setAttribute("style", "display: none;")
+        if (jsdata.keys[pmc] && jsdata.keys[pmc].length > 0) {
+            dialog.innerHTML = "<p>Suggested JIRA Keys: <kbd>" + 
jsdata.keys[pmc].join(", ") + "</kbd></p>"
+        } else {
+            dialog.innerHTML = "<p>No JIRA keys found - are you sure this 
project uses JIRA?</p>"
+        }
+               dialog.innerHTML += "<form><b>JIRA Project:</b><input 
type='text' name='jira' placeholder='FOO'><br><b>Optional prepend:</b> <input 
name='prepend' type='text' placeholder='Foo'/><br>"+
+                                  "<input type='button' value='Fetch from 
JIRA' onclick='fetchJIRA(\"" + pmc + "\", this.form[\"jira\"].value, 
this.form[\"prepend\"].value);'></form>"+
+                                  "<p>If you have multiple JIRA projects and 
they only have the version number in their release versions, please enter the 
component name in the 'prepend' field.</p>"
+               document.getElementById('tab_' + pmc).appendChild(dialog)
+
+               // Manually add release dialog
+               var rdialog = document.createElement('div');
+               rdialog.setAttribute("id", "rdialog_" + pmc);
+               rdialog.setAttribute("title", "Add a release for " + pmc)
+               rdialog.setAttribute("style", "display: none;")
+               rdialog.innerHTML = "<form><b>Version:</b><input type='text' 
name='version' placeholder='1.2.3'><br>"+
+                                   "<b>Date:</b> <input name='date' 
type='text' placeholder='YYYY-MM-DD'/><br>"+
+                                   "<input type='button' value='Add release' 
onclick='addRelease(\"" + pmc + "\", this.form[\"version\"].value, 
this.form[\"date\"].value);'></form>"
+               document.getElementById('tab_' + pmc).appendChild(rdialog)
+
+       }
+       if (json.pmcs.length == 0) {
+               container.innerHTML = "You are not a member of any PMC, sorry!"
+       }
+
+       $("#tabcontents").find("[id^='tab']").hide();
+
+
+
+       $('#tabs a').click(function(e) {
+               e.preventDefault();
+               if ($(this).closest("li").attr("id") == "current") {
+                       return;
+               } else {
+                       $("#tabcontents").find("[id^='tab_']").hide();
+                       $("#tabs li").attr("id", "");
+                       $(this).parent().attr("id", "current");
+                       $('#' + $(this).attr('name')).fadeIn();
+               }
+       });
+
+       var project = nproject ? nproject : document.location.search.substr(1);
+
+       if (project && project.length > 0) {
+               $("#tabcontents #tab_" + project).fadeIn();
+               $("#tabs #btn_" + project).attr('id', 'current');
+       }
+       if (json.all && json.all.length > 0) {
+               var btn = document.createElement('li');
+               btn.setAttribute("style", "margin-left: 48px;")
+               btn.setAttribute("id", "btn_all")
+               btn.setAttribute("class", "tab-title")
+               if (json.all.indexOf("-----------------------") == -1) {
+                       json.all.sort()
+                       json.all.unshift("-----------------------")
+                       json.all.unshift("Members-only Quick-nav:")
+               }
+
+               var sel = makeSelect("project", json.all)
+               sel.setAttribute("style", "height: 32px !important; padding: 
0px !important; margin: 0px !important; margin-left: 32px !important;")
+               sel.style = "break-before: never; break-after: never; float: 
left"
+               sel.setAttribute("onchange", "GetAsyncJSON('getjson.py?only='+ 
this.value, this.value, mergeData);")
+               btn.appendChild(sel)
+               panellist.appendChild(btn)
+
+       }
+
+
+
+}
+
+// Called by: GetAsyncJSON('getjson.py?only='+ this.value, this.value, 
mergeData) 
+
+function mergeData(json, pmc) {
+       if (jsdata.pmcs.indexOf(pmc) >= 0) {
+               return
+       }
+       if (nproject && nproject.length > 0) {
+               for (i in jsdata.pmcs) {
+                       if (jsdata.pmcs[i] == nproject) {
+                               jsdata.pmcs.splice(i, 1);
+                               break
+                       }
+               }
+       }
+
+       var todo = new Array('count', 'mail', 'delivery', 'bugzilla', 'jira', 
'changes', 'pmcdates', 'pdata', 'releases', 'keys', 'health', 'checker')
+       for (i in todo) {
+               var key = todo[i]
+               jsdata[key][pmc] = json[key][pmc];
+       }
+       jsdata.pmcs.push(pmc)
+       nproject = pmc
+       renderFrontPage(jsdata)
+}
+
+
+function renderJIRA(pmc) {
+       var obj = buildPanel(pmc, "JIRA Statistics")
+
+       addLine(pmc, "## JIRA activity:")
+       addLine(pmc)
+       addLine(pmc, " - " + jsdata.jira[pmc][0] + " JIRA tickets created in 
the last 3 months");
+       addLine(pmc, " - " + jsdata.jira[pmc][1] + " JIRA tickets 
closed/resolved in the last 3 months");
+       addLine(pmc)
+       obj.innerHTML += jsdata.jira[pmc][0] + " JIRA tickets created in the 
last 3 months<br>";
+       obj.innerHTML += jsdata.jira[pmc][1] + " JIRA tickets closed/resolved 
in the last 3 months<br>";
+       if (jsdata.keys[pmc]) {
+               obj.innerHTML += "Keys used: <kbd>" + jsdata.keys[pmc].join(", 
") + "</kbd><br>"
+       }
+       obj.innerHTML += "Keys with tickets: <kbd>" + 
jsdata.jira[pmc][2].join(", ") + "</kbd>"
+
+}
+
+
+function renderBZ(pmc) {
+    var obj = buildPanel(pmc, "Bugzilla Statistics")
+
+    addLine(pmc, "## Bugzilla Statistics:")
+    addLine(pmc)
+    addLine(pmc, " - " + jsdata.bugzilla[pmc][0] + " Bugzilla tickets created 
in the last 3 months");
+    addLine(pmc, " - " + jsdata.bugzilla[pmc][1] + " Bugzilla tickets resolved 
in the last 3 months");
+    addLine(pmc)
+    obj.innerHTML += jsdata.bugzilla[pmc][0] + " Bugzilla tickets created in 
the last 3 months<br>";
+    obj.innerHTML += jsdata.bugzilla[pmc][1] + " Bugzilla tickets resolved in 
the last 3 months<br>";
+    obj.innerHTML += "Tickets were found for the following products:<br><kbd>" 
+ Object.keys(jsdata.bugzilla[pmc][2]).sort().join(", ") + "</kbd>"
+}
+
+function renderChart(json, name, container, delivery) {
+
+       var chartDiv = document.createElement('div')
+       chartDiv.setAttribute("id", name + "_chart")
+       var dates = []
+       var noweekly = 0;
+       for (date in json[name]) {
+               dates.push(date)
+       }
+       for (date in delivery) noweekly++;
+       var d = name.split(".org-");
+       var mlname = d[1] + "@" + d[0] + ".org"
+       dates.sort();
+       var cu = 0;
+       var narr = []
+       var hitFirst = false
+
+       var dp = new Date();
+       dp.setMonth(dp.getMonth() - 3);
+
+       var odp = new Date();
+       odp.setMonth(odp.getMonth() - 6);
+
+       var difference = 0
+       for (i in dates) {
+               var date = dates[i];
+               var dateString = new Date(parseInt(date) * 1000);
+               if (dateString > dp) {
+                       difference += json[name][date]
+               }
+               cu = cu + json[name][date];
+               if (cu > 0) {
+                       hitFirst = true
+               }
+               if ((cu > 0 || hitFirst) && dateString >= odp) {
+                       if (noweekly > 0) {
+                               narr.push([dateString, cu, delivery[date] ? 
delivery[date] : 0])
+                       } else {
+                               narr.push([dateString, cu])
+                       }
+               }
+
+       }
+
+       var data = new google.visualization.DataTable();
+       data.addColumn('date', 'Date');
+       data.addColumn('number', "List members");
+       if (noweekly > 0) {
+               data.addColumn('number', "Emails sent per week");
+       }
+
+       data.addRows(narr);
+
+
+       var options = {
+               title: 'Mailing list stats for ' + mlname,
+               backgroundColor: 'transparent',
+               width: 900,
+               height: 260,
+               legend: {
+                       position: 'none',
+                       maxLines: 3
+               },
+               vAxis: {
+                       format: "#"
+               },
+               vAxes: (noweekly > 0) ? [
+
+                       {
+                               title: 'Emails per week',
+                               titleTextStyle: {
+                                       color: '#DD0000'
+                               },
+                               min: 0
+                       }, {
+                               title: 'Subscribers',
+                               titleTextStyle: {
+                                       color: '#0000DD'
+                               },
+                               min: 0,
+                               minValue: 0
+                       },
+               ] : [{
+                               title: 'Subscribers',
+                               titleTextStyle: {
+                                       color: '#0000DD'
+                               }
+                       },
+               ],
+               series: {
+                       0: {
+                               type: "line",
+                               pointSize: 4,
+                               lineWidth: 2,
+                               targetAxisIndex: (noweekly > 0) ? 1 : null
+                       },
+                       1: {
+                               type: "bars",
+                               targetAxisIndex: (noweekly > 0) ? 0 : [0, 1]
+                       }
+               },
+               seriesType: "bars",
+               tooltip: {
+                       isHtml: true
+               },
+       };
+
+       var chart = new google.visualization.ComboChart(chartDiv);
+
+       chart.draw(data, options);
+       return [cu, difference, chartDiv];
+
+}
+
+
+
+function renderReleaseChart(releases, name, container) {
+
+
+       var chartDiv;
+       if (document.getElementById(name + "_releasechart")) {
+               chartDiv = document.getElementById(name + "_releasechart")
+       } else {
+               chartDiv = document.createElement('div')
+               chartDiv.setAttribute("id", name + "_releasechart")
+       }
+
+       var narr = []
+       var maxLen = 1;
+       for (version in releases) {
+               var x = version.match(/(\d+)\.(\d+)/)
+               if (x && x[2].length > maxLen) {
+                       maxLen = x[2].length;
+               }
+       }
+       for (version in releases) {
+               if (new Date(releases[version] * 1000).getFullYear() >= 1999) {
+                       var major = parseFloat(version) ? parseFloat(version) : 
1
+                       var x = version.match(/(\d+)\.(\d+)/)
+                       if (x) {
+                               while (x[2].length < maxLen) {
+                                       x[2] = "0" + x[2]
+                               }
+                               major = parseFloat(x[1] + "." + x[2])
+                       }
+                       narr.push([new Date(releases[version] * 1000), major, 
version + " - " + new Date(releases[version] * 1000).toDateString()])
+               }
+
+       }
+
+       var data = new google.visualization.DataTable();
+
+       data.addColumn('datetime', 'Date');
+       data.addColumn('number', 'Version')
+       data.addColumn('string', 'tooltip');
+       data.setColumnProperty(2, 'role', 'tooltip');
+
+       data.addRows(narr);
+
+
+       var options = {
+               title: 'Release timeline for ' + name,
+               height: 240,
+               width: 800,
+               backgroundColor: 'transparent',
+               series: [{
+                               pointSize: 15
+                       },
+               ],
+               pointShape: {
+                       type: 'star',
+                       sides: 5
+               }
+       };
+
+       var chart = new google.visualization.ScatterChart(chartDiv);
+       chartDiv.style.marginLeft = "50px";
+
+       chart.draw(data, options);
+       return chartDiv
+}
+
+function fetchJIRA(pmc, project, prepend) {
+       if (project && project.length > 1) {
+               GetAsyncJSON("jiraversions.py?project=" + pmc + "&jiraname=" + 
project + "&prepend=" + prepend, null, function(json) {
+                       if (json && json.versions) {
+                               for (version in json.versions) {
+                                       jsdata.releases[pmc][version] = 
json.versions[version]
+                               }
+                               $('#dialog_' + pmc).dialog("close")
+                               nproject = pmc
+                               alert("Fetched " + json.added + " releases from 
JIRA!")
+                               renderFrontPage(jsdata)
+
+            } else if (json && json.status){
+                alert(json.status)
+            } else if (json) {
+                alert(JSON.stringify(json))
+                       } else {
+                               alert("Couldn't find any release data :(")
+                       }
+               })
+       }
+
+}
+
+function addRelease(pmc, version, date) {
+       if (version && version.length > 1 && 
date.match(/^(\d\d\d\d)-(\d\d)-(\d\d)$/)) {
+               var x = date.split("-");
+               var y = new Date(x[0], parseInt(x[1]) - 1, parseInt(x[2]));
+               var nn = parseInt(y.getTime() / 1000);
+               var now = (new Date().getTime()) / 1000;
+               if (nn >= now) {
+                   alert("Date is in the future!")
+                   return
+               }
+               GetAsyncJSON("addrelease.py?json=true&committee=" + pmc + 
"&version=" + escape(version) + "&date=" + nn, null, function(json) {
+                       if (json && json.versions) {
+                               var n = 0;
+                               for (version in json.versions) {
+                                       n++;
+                                       jsdata.releases[pmc][version] = 
json.versions[version]
+                               }
+                               $('#rdialog_' + pmc).dialog("close")
+                               nproject = pmc
+                               alert("Release added!")
+                               renderFrontPage(jsdata)
+
+            } else if (json && json.status){
+                alert(json.status)
+            } else if (json) {
+                alert(JSON.stringify(json))
+                       } else {
+                               alert("Couldn't add release data :(")
+                       }
+               })
+       }
+
+}


Reply via email to