community-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From humbed...@apache.org
Subject svn commit: r1839221 - in /comdev/project-logos: site/ site/css/ site/images/ site/js/ site/tools/ tools/
Date Sun, 26 Aug 2018 14:42:12 GMT
Author: humbedooh
Date: Sun Aug 26 14:42:11 2018
New Revision: 1839221

URL: http://svn.apache.org/viewvc?rev=1839221&view=rev
Log:
Initial checkout of site and generator

Added:
    comdev/project-logos/site/
    comdev/project-logos/site/css/
    comdev/project-logos/site/css/logofinder.css
    comdev/project-logos/site/images/
    comdev/project-logos/site/images/eps.png   (with props)
    comdev/project-logos/site/images/pdf.png   (with props)
    comdev/project-logos/site/images/png.png   (with props)
    comdev/project-logos/site/index.html
    comdev/project-logos/site/js/
    comdev/project-logos/site/js/base.js
    comdev/project-logos/site/js/http.js
    comdev/project-logos/site/js/logofinder.js
    comdev/project-logos/site/js/scaffolding.js
    comdev/project-logos/site/tools/
    comdev/project-logos/site/tools/compile.py
    comdev/project-logos/tools/
    comdev/project-logos/tools/compile.py

Added: comdev/project-logos/site/css/logofinder.css
URL: http://svn.apache.org/viewvc/comdev/project-logos/site/css/logofinder.css?rev=1839221&view=auto
==============================================================================
--- comdev/project-logos/site/css/logofinder.css (added)
+++ comdev/project-logos/site/css/logofinder.css Sun Aug 26 14:42:11 2018
@@ -0,0 +1,81 @@
+body {
+    background-color: #EEE;
+    color: #222;
+    line-height: 1.25 !important;
+}
+
+.project_rect {
+    color: #000;
+    width: 400px;
+    height: 340px;
+    text-align: center;
+    font-family: sans-serif;
+    border: 1.5px solid #3338;
+    border-radius: 5px;
+    background-color: #FFF;
+    display: inline-block;
+    margin: 10px;
+    font-size: 12px;
+    position: relative;
+    overflow: hidden;
+}
+
+#intro {
+    margin: 10px auto;
+    color: #000;
+    width: 1000px;
+    height: 80px;
+    text-align: center;
+    font-family: sans-serif;
+    font-size: 12px;
+    border: 1.5px solid #3338;
+    border-radius: 5px;
+    background-color: #FFFC;
+    padding: 4px;
+    position: relative;
+    overflow: hidden;
+}
+
+.project_logo {
+    width: 400px;
+    height: 160px;
+    position: relative;
+    vertical-align: middle;
+    line-height: 180px;
+    text-align: center;
+}
+
+.project_rect > p {
+    font-size: 11px;
+    padding: 2px;
+}
+
+form {
+    font-family: sans-serif;
+    font-size: 16px;
+    text-align: center;
+}
+
+h3 {
+    text-align: center;
+    font-family: sans-serif;
+    color: #333;
+}
+
+.img_download {
+    border: 1px solid #3332;
+    margin: 2px;
+    padding: 2px;
+    font-size: 10px;
+    font-family: sans-serif;
+    display: inline-block;
+    overflow: hidden;
+    width: 105px;
+    line-height: 1.1;
+    height: 50px;
+    text-align: center;
+}
+
+.img_download:hover {
+    background: #FFC;
+}
\ No newline at end of file

Added: comdev/project-logos/site/images/eps.png
URL: http://svn.apache.org/viewvc/comdev/project-logos/site/images/eps.png?rev=1839221&view=auto
==============================================================================
Binary file - no diff available.

Propchange: comdev/project-logos/site/images/eps.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: comdev/project-logos/site/images/pdf.png
URL: http://svn.apache.org/viewvc/comdev/project-logos/site/images/pdf.png?rev=1839221&view=auto
==============================================================================
Binary file - no diff available.

Propchange: comdev/project-logos/site/images/pdf.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: comdev/project-logos/site/images/png.png
URL: http://svn.apache.org/viewvc/comdev/project-logos/site/images/png.png?rev=1839221&view=auto
==============================================================================
Binary file - no diff available.

Propchange: comdev/project-logos/site/images/png.png
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: comdev/project-logos/site/index.html
URL: http://svn.apache.org/viewvc/comdev/project-logos/site/index.html?rev=1839221&view=auto
==============================================================================
--- comdev/project-logos/site/index.html (added)
+++ comdev/project-logos/site/index.html Sun Aug 26 14:42:11 2018
@@ -0,0 +1,133 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <title>Apache Project logos</title>
+    <link rel="apple-touch-icon" sizes="57x57" href="/favicons/apple-touch-icon-57x57.png">
+  <link rel="apple-touch-icon" sizes="60x60" href="/favicons/apple-touch-icon-60x60.png">
+  <link rel="apple-touch-icon" sizes="72x72" href="/favicons/apple-touch-icon-72x72.png">
+  <link rel="apple-touch-icon" sizes="76x76" href="/favicons/apple-touch-icon-76x76.png">
+  <link rel="apple-touch-icon" sizes="114x114" href="/favicons/apple-touch-icon-114x114.png">
+  <link rel="apple-touch-icon" sizes="120x120" href="/favicons/apple-touch-icon-120x120.png">
+  <link rel="apple-touch-icon" sizes="144x144" href="/favicons/apple-touch-icon-144x144.png">
+  <link rel="apple-touch-icon" sizes="152x152" href="/favicons/apple-touch-icon-152x152.png">
+  <link rel="apple-touch-icon" sizes="180x180" href="/favicons/apple-touch-icon-180x180.png">
+  <link rel="icon" type="image/png" href="/favicons/favicon-32x32.png" sizes="32x32">
+  <link rel="icon" type="image/png" href="/favicons/favicon-194x194.png" sizes="194x194">
+  <link rel="icon" type="image/png" href="/favicons/favicon-96x96.png" sizes="96x96">
+  <link rel="icon" type="image/png" href="/favicons/android-chrome-192x192.png" sizes="192x192">
+  <link rel="icon" type="image/png" href="/favicons/favicon-16x16.png" sizes="16x16">
+  <link rel="manifest" href="/favicons/manifest.json">
+  <link rel="shortcut icon" href="/favicons/favicon.ico">
+  <meta name="msapplication-TileColor" content="#603cba">
+  <meta name="msapplication-TileImage" content="/favicons/mstile-144x144.png">
+  <meta name="msapplication-config" content="/favicons/browserconfig.xml">
+  <meta name="theme-color" content="#303284">
+
+  <title>Welcome to The Apache Software Foundation!</title>
+  <link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700%7cDroid+Serif:400,700' rel='stylesheet' type='text/css'>
+  <link href="/css/min.bootstrap.css" rel="stylesheet">
+  <link href="/css/styles.css" rel="stylesheet">  
+    <link rel="stylesheet" type="text/css" href="css/logofinder.css">
+    
+  </head>
+  <body onload="init_projects();">
+    
+    <!-- Navigation -->  
+    <header>
+      <nav class="navbar navbar-default navbar-fixed-top">
+        <div class="container">
+          <div class="navbar-header">
+            <button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#mainnav-collapse">
+              <span class="sr-only">Toggle navigation</span>
+              <span class="icon-bar"></span>
+              <span class="icon-bar"></span>
+              <span class="icon-bar"></span>
+            </button>
+            <a href="#" class="navbar-brand"><span class="glyphicon glyphicon-home"></span></a>
+          </div>
+          <div class="collapse navbar-collapse" id="mainnav-collapse">
+            <div style="line-height:20px; padding-top:5px; float:left"><a href="/">Home</a></div>
+            <ul class="nav navbar-nav navbar-right">
+              <li class="dropdown">
+                <a href="#" class="dropdown-toggle" data-toggle="dropdown">About <span class="caret"></span></a>
+                <ul class="dropdown-menu" role="menu">
+                  <li><a href="/foundation">Overview</a></li>
+                  <li><a href="/foundation/members.html">Members</a></li>
+                  <li><a href="/foundation/how-it-works.html">Process</a></li>
+                  <li><a href="/foundation/sponsorship.html">Sponsorship</a></li>
+                  <li><a href="/foundation/glossary.html">Glossary</a></li>
+                  <li><a href="/foundation/preFAQ.html">FAQ</a></li>
+                  <li><a href="/foundation/policies/conduct.html">Code of Conduct</a></li>
+                  <li><a href="/foundation/contact.html ">Contact</a></li>      				
+                </ul>
+              </li>
+          <li><a href="/index.html#projects-list">Projects</a></li>
+              <li class="dropdown">
+                <a href="#" class="dropdown-toggle" data-toggle="dropdown">People <span class="caret"></span></a>
+                <ul class="dropdown-menu" role="menu">
+                  <li><a href="http://people.apache.org/">Overview</a></li>
+                  <li><a href="http://people.apache.org/committer-index.html">Committers</a></li>
+                  <li><a href="/foundation/how-it-works.html#meritocracy">Meritocracy</a></li>
+                  <li><a href="/foundation/how-it-works.html#roles">Roles</a></li>
+                  <li><a href="/foundation/policies/conduct.html">Code of Conduct</a></li>
+                  <li><a href="http://planet.apache.org/">Planet Apache</a></li>
+                </ul>
+              </li>
+              <li class="dropdown">
+                <a href="#" class="dropdown-toggle" data-toggle="dropdown">Get Involved <span class="caret"></span></a>
+                <ul class="dropdown-menu" role="menu">
+                  <li><a href="/foundation/getinvolved.html">Overview</a></li>
+                  <li><a href="http://community.apache.org/">Community Development</a></li>
+                  <li><a href="/foundation/policies/conduct.html">Code of Conduct</a></li>
+                        <li><a href="http://helpwanted.apache.org/">Help Wanted</a></li>
+                  <li><a href="http://www.apachecon.com/">ApacheCon</a></li>
+                </ul>
+              </li>
+              <li><a href="/dyn/closer.cgi">Download</a></li>
+              <li class="dropdown">
+                <a href="#" class="dropdown-toggle" data-toggle="dropdown">Support Apache <span class="caret"></span></a>
+                <ul class="dropdown-menu" role="menu">
+                  <li><a href="/foundation/sponsorship.html">Sponsorship</a></li>
+                  <li><a href="/foundation/contributing.html">Donations</a></li>
+                  <li><a href="/foundation/buy_stuff.html">Buy Stuff</a></li>
+                  <li><a href="/foundation/thanks.html">Thanks</a></li>
+                </ul>
+              </li>
+            </ul>
+          </div>
+        </div>
+      </nav>
+    </header>
+    <!-- / Navigation -->
+    
+    
+    <form>
+      <h1>Apache Project Logos</h1>
+      Find a project: <input type="text" id="pkey" onkeyup="find_project(this.value);"/>
+      &nbsp;
+      <input type="button" value="reset" onclick="document.getElementById('pkey').value=''; find_project('');"/>
+    </form>
+    <div id="intro">
+      <b>How do I get my project logo on this page?</b><br/>
+      Commit your original logo (svg, eps, ai, pdf accepted) to
+      <a href="https://svn.apache.org/repos/asf/comdev/project-logos/originals/">this location</a>, and call it $project.$ext, for instance couchdb.svg.
+      If you have multiple versions to display (for instance a vertical and horizontal version), you can call them $project-1.ext, $project-2.ext and so on,
+      and the site will pick up on that. 
+      You can also send it or a query to dev@community.apache.org if you need further assistance.
+      This site is automatically regenerated every hour.
+    </div>
+    <div id="wrapper">
+      
+    </div>
+    <script src="js/base.js" type="text/javascript"></script>
+    <script src="js/http.js" type="text/javascript"></script>
+    <script src="js/scaffolding.js" type="text/javascript"></script>
+    <script src="js/logofinder.js" type="text/javascript"></script>
+    
+    <script src="/js/jquery-2.1.1.min.js"></script>
+    <script src="/js/bootstrap.js"></script>
+  </body>
+</html>
+

Added: comdev/project-logos/site/js/base.js
URL: http://svn.apache.org/viewvc/comdev/project-logos/site/js/base.js?rev=1839221&view=auto
==============================================================================
--- comdev/project-logos/site/js/base.js (added)
+++ comdev/project-logos/site/js/base.js Sun Aug 26 14:42:11 2018
@@ -0,0 +1,139 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+/**
+ * String formatting prototype
+ * A'la printf
+ */
+
+String.prototype.format = function() {
+  let args = arguments;
+  let n = 0;
+  let t = this;
+  let rtn = this.replace(/(?!%)?%([-+]*)([0-9.]*)([a-zA-Z])/g, function(m, pm, len, fmt) {
+      len = parseInt(len || '1');
+      // We need the correct number of args, balk otherwise, using ourselves to format the error!
+      if (args.length <= n) {
+        let err = "Error interpolating string '%s': Expected at least %u argments, only got %u!".format(t, n+1, args.length);
+        console.log(err);
+        throw err;
+      }
+      let varg = args[n];
+      n++;
+      switch (fmt) {
+        case 's':
+          if (typeof(varg) == 'function') {
+            varg = '(function)';
+          }
+          return varg;
+        // For now, let u, d and i do the same thing
+        case 'd':
+        case 'i':
+        case 'u':
+          varg = parseInt(varg).pad(len); // truncate to Integer, pad if needed
+          return varg;
+      }
+    });
+  return rtn;
+}
+
+
+/**
+ * Number prettification prototype:
+ * Converts 1234567 into 1,234,567 etc
+ */
+
+Number.prototype.pretty = function(fix) {
+  if (fix) {
+    return String(this.toFixed(fix)).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
+  }
+  return String(this.toFixed(0)).replace(/(\d)(?=(\d{3})+$)/g, '$1,');
+};
+
+
+/**
+ * Number padding
+ * usage: 123.pad(6) -> 000123
+ */
+
+Number.prototype.pad = function(n) {
+  var str;
+  str = String(this);
+
+  /* Do we need to pad? if so, do it using String.repeat */
+  if (str.length < n) {
+    str = "0".repeat(n - str.length) + str;
+  }
+  return str;
+};
+
+
+/* Func for converting a date to YYYY-MM-DD HH:MM */
+
+Date.prototype.ISOBare = function() {
+  var M, d, h, m, y;
+  y = this.getFullYear();
+  m = (this.getMonth() + 1).pad(2);
+  d = this.getDate().pad(2);
+  h = this.getHours().pad(2);
+  M = this.getMinutes().pad(2);
+  return y + "-" + m + "-" + d + " " + h + ":" + M;
+};
+
+
+/* isArray: function to detect if an object is an array */
+
+isArray = function(value) {
+  return value && typeof value === 'object' && value instanceof Array && typeof value.length === 'number' && typeof value.splice === 'function' && !(value.propertyIsEnumerable('length'));
+};
+
+
+/* isHash: function to detect if an object is a hash */
+
+isHash = function(value) {
+  return value && typeof value === 'object' && !isArray(value);
+};
+
+
+/* Remove an array element by value */
+
+Array.prototype.remove = function(val) {
+  var i, item, j, len;
+  for (i = j = 0, len = this.length; j < len; i = ++j) {
+    item = this[i];
+    if (item === val) {
+      this.splice(i, 1);
+      return this;
+    }
+  }
+  return this;
+};
+
+
+/* Check if array has value */
+Array.prototype.has = function(val) {
+  var i, item, j, len;
+  for (i = j = 0, len = this.length; j < len; i = ++j) {
+    item = this[i];
+    if (item === val) {
+      return true;
+    }
+  }
+  return false;
+};
+
+

Added: comdev/project-logos/site/js/http.js
URL: http://svn.apache.org/viewvc/comdev/project-logos/site/js/http.js?rev=1839221&view=auto
==============================================================================
--- comdev/project-logos/site/js/http.js (added)
+++ comdev/project-logos/site/js/http.js Sun Aug 26 14:42:11 2018
@@ -0,0 +1,762 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+/**
+ * String formatting prototype
+ * A'la printf
+ */
+
+String.prototype.format = function() {
+  let args = arguments;
+  let n = 0;
+  let t = this;
+  let rtn = this.replace(/(?!%)?%([-+]*)([0-9.]*)([a-zA-Z])/g, function(m, pm, len, fmt) {
+      len = parseInt(len || '1');
+      // We need the correct number of args, balk otherwise, using ourselves to format the error!
+      if (args.length <= n) {
+        let err = "Error interpolating string '%s': Expected at least %u argments, only got %u!".format(t, n+1, args.length);
+        console.log(err);
+        throw err;
+      }
+      let varg = args[n];
+      n++;
+      switch (fmt) {
+        case 's':
+          if (typeof(varg) == 'function') {
+            varg = '(function)';
+          }
+          return varg;
+        // For now, let u, d and i do the same thing
+        case 'd':
+        case 'i':
+        case 'u':
+          varg = parseInt(varg).pad(len); // truncate to Integer, pad if needed
+          return varg;
+      }
+    });
+  return rtn;
+}
+
+
+/**
+ * Number prettification prototype:
+ * Converts 1234567 into 1,234,567 etc
+ */
+
+Number.prototype.pretty = function(fix) {
+  if (fix) {
+    return String(this.toFixed(fix)).replace(/(\d)(?=(\d{3})+\.)/g, '$1,');
+  }
+  return String(this.toFixed(0)).replace(/(\d)(?=(\d{3})+$)/g, '$1,');
+};
+
+
+/**
+ * Number padding
+ * usage: 123.pad(6) -> 000123
+ */
+
+Number.prototype.pad = function(n) {
+  var str;
+  str = String(this);
+
+  /* Do we need to pad? if so, do it using String.repeat */
+  if (str.length < n) {
+    str = "0".repeat(n - str.length) + str;
+  }
+  return str;
+};
+
+
+/* Func for converting a date to YYYY-MM-DD HH:MM */
+
+Date.prototype.ISOBare = function() {
+  var M, d, h, m, y;
+  y = this.getFullYear();
+  m = (this.getMonth() + 1).pad(2);
+  d = this.getDate().pad(2);
+  h = this.getHours().pad(2);
+  M = this.getMinutes().pad(2);
+  return y + "-" + m + "-" + d + " " + h + ":" + M;
+};
+
+
+/* isArray: function to detect if an object is an array */
+
+isArray = function(value) {
+  return value && typeof value === 'object' && value instanceof Array && typeof value.length === 'number' && typeof value.splice === 'function' && !(value.propertyIsEnumerable('length'));
+};
+
+
+/* isHash: function to detect if an object is a hash */
+
+isHash = function(value) {
+  return value && typeof value === 'object' && !isArray(value);
+};
+
+
+/* Remove an array element by value */
+
+Array.prototype.remove = function(val) {
+  var i, item, j, len;
+  for (i = j = 0, len = this.length; j < len; i = ++j) {
+    item = this[i];
+    if (item === val) {
+      this.splice(i, 1);
+      return this;
+    }
+  }
+  return this;
+};
+
+
+/* Check if array has value */
+Array.prototype.has = function(val) {
+  var i, item, j, len;
+  for (i = j = 0, len = this.length; j < len; i = ++j) {
+    item = this[i];
+    if (item === val) {
+      return true;
+    }
+  }
+  return false;
+};
+
+
+
+
+// URL calls currently 'in escrow'. This controls the spinny wheel animation
+var async_escrow = {}
+var async_maxwait = 250; // ms to wait before displaying spinner
+var async_status = 'clear';
+var async_cache = {}
+
+// Escrow spinner check
+async function escrow_check() {
+    let now = new Date();
+    let show_spinner = false;
+    for (var k in async_escrow) {
+        if ( (now - async_escrow[k]) > async_maxwait ) {
+            show_spinner = true;
+            break;
+        }
+    }
+    // Fetch or create the spinner
+    let spinner = document.getElementById('spinner');
+    if (!spinner) {
+        spinner = new HTML('div', { id: 'spinner', class: 'spinner'});
+        spinwheel = new HTML('div', {id: 'spinwheel', class: 'spinwheel'});
+        spinner.inject(spinwheel);
+        spinner.inject(new HTML('h2', {}, "Loading, please wait.."));
+        document.body.appendChild(spinner);
+    }
+    // Show or don't show spinner?
+    if (show_spinner) {
+        spinner.style.display = 'block';
+        if (async_status === 'clear') {
+            console.log("Waiting for JSON resource, deploying spinner");
+            async_status = 'waiting';
+        }
+    } else {
+        spinner.style.display = 'none';
+        if (async_status === 'waiting') {
+            console.log("All URLs out of escrow, dropping spinner");
+            async_status = 'clear';
+        }
+    }
+}
+
+async function async_snap(error) {
+    msg = await error.text();
+    msg = (msg||"An unknown error occured, possibly an internal browser issue").replace(/<.*?>/g, ""); // strip HTML tags
+    modal("An error occured", "An error code %u occured while trying to fetch %s:\n%s".format(error.status, error.url, msg), "error");
+}
+
+
+// Asynchronous GET call
+async function GET(url, callback, state, snap, method, body) {
+    method = method || 'get'
+    console.log("Fetching JSON resource at %s".format(url))
+    let pkey = "GET-%s-%s".format(callback, url);
+    let res = undefined;
+    let res_json = undefined;
+    state = state || {};
+    state.url = url;
+    if (state && state.cached === true && async_cache[url]) {
+        console.log("Fetching %s from cache".format(url));
+        res_json = async_cache[url];
+    }
+    else {
+        try {
+            let meta = {method: method, credentials: 'include', referrerPolicy: 'unsafe-url', headers: {'x-original-referral': document.referrer}};
+            if (body) {
+                meta.body = body;
+            }
+            console.log("putting %s in escrow...".format(url));
+            async_escrow[pkey] = new Date(); // Log start of request in escrow dict
+            const rv = await fetch(url, meta); // Wait for resource...
+
+            // Since this is an async request, the request may have been canceled
+            // by the time we get a response. Only do callback if not.
+            if (async_escrow[pkey] !== undefined) {
+                delete async_escrow[pkey]; // move out of escrow when fetched
+                res = rv;
+            }
+        }
+        catch (e) {
+            delete async_escrow[pkey]; // move out of escrow if failed
+            console.log("The URL %s could not be fetched: %s".format(url, e));
+            if (snap) snap({}, {reason: e});
+            else {
+                modal("An error occured", "An error occured while trying to fetch %s:\n%s".format(url, e), "error");
+            }
+        }
+    }
+    if (res !== undefined || res_json !== undefined) {
+        // We expect a 2xx return code (usually 200 or 201), snap otherwise
+        if ((res_json) || (res.status >= 200 && res.status < 300)) {
+            console.log("Successfully fetched %s".format(url))
+            if (res_json) {
+                js = res_json;
+            } else {
+                js = await res.json();
+                async_cache[url] = js;
+            }
+            if (callback) {
+                callback(state, js);
+            } else {
+                console.log("No callback function was registered for %s, ignoring result.".format(url));
+            }
+        } else {
+            console.log("URL %s returned HTTP code %u, snapping!".format(url, res.status));
+            try {
+                js = await res.json();
+                snap(state, js);
+                return;
+            } catch (e) {}
+            if (snap) snap(res);
+            else async_snap(res);
+        }
+    }
+}
+
+
+// DELETE wrapper
+async function DELETE(url, callback, state, snap) {
+    return GET(url, callback, state, snap, 'delete');
+}
+
+// POST wrapper
+async function POST(url, callback, state, snap, json) {
+    return GET(url, callback, state, snap, 'post', JSON.stringify(json));
+}
+
+// PUT wrapper
+async function PUT(url, callback, state, snap, json) {
+    return GET(url, callback, state, snap, 'put', JSON.stringify(json));
+}
+
+// PATCH wrapper
+async function PATCH(url, callback, state, snap, json) {
+    return GET(url, callback, state, snap, 'PATCH', JSON.stringify(json));
+}
+
+// whatwg fetch for IE
+(function(self) {
+  'use strict';
+
+  if (self.fetch) {
+    return
+  }
+
+  var support = {
+    searchParams: 'URLSearchParams' in self,
+    iterable: 'Symbol' in self && 'iterator' in Symbol,
+    blob: 'FileReader' in self && 'Blob' in self && (function() {
+      try {
+        new Blob()
+        return true
+      } catch(e) {
+        return false
+      }
+    })(),
+    formData: 'FormData' in self,
+    arrayBuffer: 'ArrayBuffer' in self
+  }
+
+  if (support.arrayBuffer) {
+    var viewClasses = [
+      '[object Int8Array]',
+      '[object Uint8Array]',
+      '[object Uint8ClampedArray]',
+      '[object Int16Array]',
+      '[object Uint16Array]',
+      '[object Int32Array]',
+      '[object Uint32Array]',
+      '[object Float32Array]',
+      '[object Float64Array]'
+    ]
+
+    var isDataView = function(obj) {
+      return obj && DataView.prototype.isPrototypeOf(obj)
+    }
+
+    var isArrayBufferView = ArrayBuffer.isView || function(obj) {
+      return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
+    }
+  }
+
+  function normalizeName(name) {
+    if (typeof name !== 'string') {
+      name = String(name)
+    }
+    if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) {
+      throw new TypeError('Invalid character in header field name')
+    }
+    return name.toLowerCase()
+  }
+
+  function normalizeValue(value) {
+    if (typeof value !== 'string') {
+      value = String(value)
+    }
+    return value
+  }
+
+  // Build a destructive iterator for the value list
+  function iteratorFor(items) {
+    var iterator = {
+      next: function() {
+        var value = items.shift()
+        return {done: value === undefined, value: value}
+      }
+    }
+
+    if (support.iterable) {
+      iterator[Symbol.iterator] = function() {
+        return iterator
+      }
+    }
+
+    return iterator
+  }
+
+  function Headers(headers) {
+    this.map = {}
+
+    if (headers instanceof Headers) {
+      headers.forEach(function(value, name) {
+        this.append(name, value)
+      }, this)
+    } else if (Array.isArray(headers)) {
+      headers.forEach(function(header) {
+        this.append(header[0], header[1])
+      }, this)
+    } else if (headers) {
+      Object.getOwnPropertyNames(headers).forEach(function(name) {
+        this.append(name, headers[name])
+      }, this)
+    }
+  }
+
+  Headers.prototype.append = function(name, value) {
+    name = normalizeName(name)
+    value = normalizeValue(value)
+    var oldValue = this.map[name]
+    this.map[name] = oldValue ? oldValue+','+value : value
+  }
+
+  Headers.prototype['delete'] = function(name) {
+    delete this.map[normalizeName(name)]
+  }
+
+  Headers.prototype.get = function(name) {
+    name = normalizeName(name)
+    return this.has(name) ? this.map[name] : null
+  }
+
+  Headers.prototype.has = function(name) {
+    return this.map.hasOwnProperty(normalizeName(name))
+  }
+
+  Headers.prototype.set = function(name, value) {
+    this.map[normalizeName(name)] = normalizeValue(value)
+  }
+
+  Headers.prototype.forEach = function(callback, thisArg) {
+    for (var name in this.map) {
+      if (this.map.hasOwnProperty(name)) {
+        callback.call(thisArg, this.map[name], name, this)
+      }
+    }
+  }
+
+  Headers.prototype.keys = function() {
+    var items = []
+    this.forEach(function(value, name) { items.push(name) })
+    return iteratorFor(items)
+  }
+
+  Headers.prototype.values = function() {
+    var items = []
+    this.forEach(function(value) { items.push(value) })
+    return iteratorFor(items)
+  }
+
+  Headers.prototype.entries = function() {
+    var items = []
+    this.forEach(function(value, name) { items.push([name, value]) })
+    return iteratorFor(items)
+  }
+
+  if (support.iterable) {
+    Headers.prototype[Symbol.iterator] = Headers.prototype.entries
+  }
+
+  function consumed(body) {
+    if (body.bodyUsed) {
+      return Promise.reject(new TypeError('Already read'))
+    }
+    body.bodyUsed = true
+  }
+
+  function fileReaderReady(reader) {
+    return new Promise(function(resolve, reject) {
+      reader.onload = function() {
+        resolve(reader.result)
+      }
+      reader.onerror = function() {
+        reject(reader.error)
+      }
+    })
+  }
+
+  function readBlobAsArrayBuffer(blob) {
+    var reader = new FileReader()
+    var promise = fileReaderReady(reader)
+    reader.readAsArrayBuffer(blob)
+    return promise
+  }
+
+  function readBlobAsText(blob) {
+    var reader = new FileReader()
+    var promise = fileReaderReady(reader)
+    reader.readAsText(blob)
+    return promise
+  }
+
+  function readArrayBufferAsText(buf) {
+    var view = new Uint8Array(buf)
+    var chars = new Array(view.length)
+
+    for (var i = 0; i < view.length; i++) {
+      chars[i] = String.fromCharCode(view[i])
+    }
+    return chars.join('')
+  }
+
+  function bufferClone(buf) {
+    if (buf.slice) {
+      return buf.slice(0)
+    } else {
+      var view = new Uint8Array(buf.byteLength)
+      view.set(new Uint8Array(buf))
+      return view.buffer
+    }
+  }
+
+  function Body() {
+    this.bodyUsed = false
+
+    this._initBody = function(body) {
+      this._bodyInit = body
+      if (!body) {
+        this._bodyText = ''
+      } else if (typeof body === 'string') {
+        this._bodyText = body
+      } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
+        this._bodyBlob = body
+      } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
+        this._bodyFormData = body
+      } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
+        this._bodyText = body.toString()
+      } else if (support.arrayBuffer && support.blob && isDataView(body)) {
+        this._bodyArrayBuffer = bufferClone(body.buffer)
+        // IE 10-11 can't handle a DataView body.
+        this._bodyInit = new Blob([this._bodyArrayBuffer])
+      } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
+        this._bodyArrayBuffer = bufferClone(body)
+      } else {
+        throw new Error('unsupported BodyInit type')
+      }
+
+      if (!this.headers.get('content-type')) {
+        if (typeof body === 'string') {
+          this.headers.set('content-type', 'text/plain;charset=UTF-8')
+        } else if (this._bodyBlob && this._bodyBlob.type) {
+          this.headers.set('content-type', this._bodyBlob.type)
+        } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
+          this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8')
+        }
+      }
+    }
+
+    if (support.blob) {
+      this.blob = function() {
+        var rejected = consumed(this)
+        if (rejected) {
+          return rejected
+        }
+
+        if (this._bodyBlob) {
+          return Promise.resolve(this._bodyBlob)
+        } else if (this._bodyArrayBuffer) {
+          return Promise.resolve(new Blob([this._bodyArrayBuffer]))
+        } else if (this._bodyFormData) {
+          throw new Error('could not read FormData body as blob')
+        } else {
+          return Promise.resolve(new Blob([this._bodyText]))
+        }
+      }
+
+      this.arrayBuffer = function() {
+        if (this._bodyArrayBuffer) {
+          return consumed(this) || Promise.resolve(this._bodyArrayBuffer)
+        } else {
+          return this.blob().then(readBlobAsArrayBuffer)
+        }
+      }
+    }
+
+    this.text = function() {
+      var rejected = consumed(this)
+      if (rejected) {
+        return rejected
+      }
+
+      if (this._bodyBlob) {
+        return readBlobAsText(this._bodyBlob)
+      } else if (this._bodyArrayBuffer) {
+        return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))
+      } else if (this._bodyFormData) {
+        throw new Error('could not read FormData body as text')
+      } else {
+        return Promise.resolve(this._bodyText)
+      }
+    }
+
+    if (support.formData) {
+      this.formData = function() {
+        return this.text().then(decode)
+      }
+    }
+
+    this.json = function() {
+      return this.text().then(JSON.parse)
+    }
+
+    return this
+  }
+
+  // HTTP methods whose capitalization should be normalized
+  var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']
+
+  function normalizeMethod(method) {
+    var upcased = method.toUpperCase()
+    return (methods.indexOf(upcased) > -1) ? upcased : method
+  }
+
+  function Request(input, options) {
+    options = options || {}
+    var body = options.body
+
+    if (input instanceof Request) {
+      if (input.bodyUsed) {
+        throw new TypeError('Already read')
+      }
+      this.url = input.url
+      this.credentials = input.credentials
+      if (!options.headers) {
+        this.headers = new Headers(input.headers)
+      }
+      this.method = input.method
+      this.mode = input.mode
+      if (!body && input._bodyInit != null) {
+        body = input._bodyInit
+        input.bodyUsed = true
+      }
+    } else {
+      this.url = String(input)
+    }
+
+    this.credentials = options.credentials || this.credentials || 'omit'
+    if (options.headers || !this.headers) {
+      this.headers = new Headers(options.headers)
+    }
+    this.method = normalizeMethod(options.method || this.method || 'GET')
+    this.mode = options.mode || this.mode || null
+    this.referrer = null
+
+    if ((this.method === 'GET' || this.method === 'HEAD') && body) {
+      throw new TypeError('Body not allowed for GET or HEAD requests')
+    }
+    this._initBody(body)
+  }
+
+  Request.prototype.clone = function() {
+    return new Request(this, { body: this._bodyInit })
+  }
+
+  function decode(body) {
+    var form = new FormData()
+    body.trim().split('&').forEach(function(bytes) {
+      if (bytes) {
+        var split = bytes.split('=')
+        var name = split.shift().replace(/\+/g, ' ')
+        var value = split.join('=').replace(/\+/g, ' ')
+        form.append(decodeURIComponent(name), decodeURIComponent(value))
+      }
+    })
+    return form
+  }
+
+  function parseHeaders(rawHeaders) {
+    var headers = new Headers()
+    // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
+    // https://tools.ietf.org/html/rfc7230#section-3.2
+    var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ')
+    preProcessedHeaders.split(/\r?\n/).forEach(function(line) {
+      var parts = line.split(':')
+      var key = parts.shift().trim()
+      if (key) {
+        var value = parts.join(':').trim()
+        headers.append(key, value)
+      }
+    })
+    return headers
+  }
+
+  Body.call(Request.prototype)
+
+  function Response(bodyInit, options) {
+    if (!options) {
+      options = {}
+    }
+
+    this.type = 'default'
+    this.status = options.status === undefined ? 200 : options.status
+    this.ok = this.status >= 200 && this.status < 300
+    this.statusText = 'statusText' in options ? options.statusText : 'OK'
+    this.headers = new Headers(options.headers)
+    this.url = options.url || ''
+    this._initBody(bodyInit)
+  }
+
+  Body.call(Response.prototype)
+
+  Response.prototype.clone = function() {
+    return new Response(this._bodyInit, {
+      status: this.status,
+      statusText: this.statusText,
+      headers: new Headers(this.headers),
+      url: this.url
+    })
+  }
+
+  Response.error = function() {
+    var response = new Response(null, {status: 0, statusText: ''})
+    response.type = 'error'
+    return response
+  }
+
+  var redirectStatuses = [301, 302, 303, 307, 308]
+
+  Response.redirect = function(url, status) {
+    if (redirectStatuses.indexOf(status) === -1) {
+      throw new RangeError('Invalid status code')
+    }
+
+    return new Response(null, {status: status, headers: {location: url}})
+  }
+
+  self.Headers = Headers
+  self.Request = Request
+  self.Response = Response
+
+  self.fetch = function(input, init) {
+    return new Promise(function(resolve, reject) {
+      var request = new Request(input, init)
+      var xhr = new XMLHttpRequest()
+
+      xhr.onload = function() {
+        var options = {
+          status: xhr.status,
+          statusText: xhr.statusText,
+          headers: parseHeaders(xhr.getAllResponseHeaders() || '')
+        }
+        options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
+        var body = 'response' in xhr ? xhr.response : xhr.responseText
+        resolve(new Response(body, options))
+      }
+
+      xhr.onerror = function() {
+        reject(new TypeError('Network request failed'))
+      }
+
+      xhr.ontimeout = function() {
+        reject(new TypeError('Network request failed'))
+      }
+
+      xhr.open(request.method, request.url, true)
+
+      if (request.credentials === 'include') {
+        xhr.withCredentials = true
+      } else if (request.credentials === 'omit') {
+        xhr.withCredentials = false
+      }
+
+      if ('responseType' in xhr && support.blob) {
+        xhr.responseType = 'blob'
+      }
+
+      request.headers.forEach(function(value, name) {
+        xhr.setRequestHeader(name, value)
+      })
+
+      xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
+    })
+  }
+  self.fetch.polyfill = true
+})(typeof self !== 'undefined' ? self : this);

Added: comdev/project-logos/site/js/logofinder.js
URL: http://svn.apache.org/viewvc/comdev/project-logos/site/js/logofinder.js?rev=1839221&view=auto
==============================================================================
--- comdev/project-logos/site/js/logofinder.js (added)
+++ comdev/project-logos/site/js/logofinder.js Sun Aug 26 14:42:11 2018
@@ -0,0 +1,182 @@
+var pjson = {};
+var logoalts = {};
+
+function change_logo(key, x) {
+    x = parseInt(x);
+    for (var i = 1; i < 10; i++) {
+        let obj = document.getElementById('logo_%s_%u'.format(key, i));
+        if (obj) {
+            if (x == i) {
+                obj.style.display = 'block';
+                let img = document.getElementById('default_%s'.format(key));
+                ext = logoalts[key][x-1];
+                if (ext == 'default') {
+                    img.src = 'res/%s/default.png'.format(key, key, ext);
+                }
+                else {
+                    img.src = 'res/%s/%s%s.png'.format(key, key, ext);
+                }
+                
+            } else {
+                obj.style.display = 'none';
+            }
+        }
+    }
+    
+}
+
+function make_div(key, project) {
+    let div = new HTML('div', { class: 'project_rect'});
+    let pname = 'Apache %s'.format(project.name);
+    if (project.podling) {
+        pname += " (incubating)";
+    }
+    let title = new HTML('h4', pname);
+    let idiv = new HTML('div', {class: 'project_logo'});
+    let img = new HTML('img', {id: 'default_%s'.format(key), src: 'res/%s/default.png'.format(key), style: {maxWidth: '320px', maxHeight: '160px'}});
+    if (project.has_default) {
+        
+    } else {
+        img = new HTML('img', {src: 'res/%s'.format(project.images[0].filename), style: {maxWidth: '260px', maxHeight: '160px'}});
+    }
+    div.inject(title);
+    idiv.inject(img);
+    div.inject(idiv);
+    
+    if (project.description && project.description.length > 0) {
+        pd = project.description;
+        if (pd.length > 150) { pd = pd.substring(0,150) + "..."}
+        div.inject(new HTML('p', [
+                                  pd + ' - ',
+                                  new HTML('a', {href: project.website}, project.website)
+                                 ]));
+        
+        
+    }
+    
+    originals = {};
+    links = new HTML('p', {style: {textAlign: 'left'}});
+    let il = {};
+    for (var i in project.images) {
+        let img = project.images[i];
+        if (img.format) {
+            if (il[img.filename]) continue;
+            il[img.filename] = true;
+            originals[img.original] = originals[img.original] || [];
+            originals[img.original].push(img);
+        }
+    }
+    let x = 0;
+    logoalts[key] = [];
+    let originals_sorted = [];
+    for (var k in originals) originals_sorted.push(k);
+    originals_sorted.sort();
+    let sel = new HTML('select', {onchange: 'change_logo("%s", this.value)'.format(key)});
+    div.inject(sel);
+    for (var z = 0; z < originals_sorted.length; z++) {
+        let original = originals_sorted[z];
+        x++;
+        let ext = 'default';
+        let m = original.match(/(-\S+)$/);
+        if (m) { ext = m[1]; }
+        logoalts[key].push(ext);
+        let odiv = new HTML('div', {id: 'logo_%s_%u'.format(key, x)});
+        let opt = new HTML('option', { value: '%u'.format(x)}, "Version %u".format(x));
+        sel.inject(opt);
+        let imgs = originals[original];
+        for (var i = 0; i < imgs.length; i++) {
+            let img = imgs[i];
+            let arr = [
+                new HTML('img', {src:'images/%s.png'.format(img.format), style: {maxWidth: '24px', maxHeight: '24px'}}),
+                new HTML('br'),
+                ];
+            let ltxt = "PNG raster";
+            if (img.format == 'pdf') {
+                ltxt = "PDF document";
+            }
+            arr.push(ltxt);
+            if (img.width && img.height) {
+                arr.push(new HTML('br'));
+                arr.push("(%s x %s pixels)".format(img.width, img.height));
+            }
+            
+            let link = new HTML('a', {href: 'res/' + img.filename},
+                                new HTML('div', {class: 'img_download'},
+                                         arr
+                                )
+                               );
+            odiv.inject(link);
+        }
+        if (x > 1) {
+            odiv.style.display = "none";
+        }
+        div.inject(odiv);
+    }
+    if (x == 1) {
+        sel.style.display = "none";
+    }
+    
+    return div;
+}
+
+function find_project(name) {
+    name = name.toLowerCase();
+    let wrapper = document.getElementById('wrapper');
+    let obj = new HTML('div');
+    wrapper.innerHTML = "";
+    let num = 0;
+    let keys = [];
+    for (var key in pjson) { keys.push(key); }
+    keys.sort();
+    for (var i = 0; i < keys.length; i++) {
+        let key = keys[i];
+        let project = pjson[key];
+        if (!project) continue;
+        if (project.name.toLowerCase().match(name) || key.toLowerCase().match(name)) {
+            if (project.has_default || project.images.length > 0) {
+                num++;
+                let div = make_div(key, project);
+                obj.inject(div);
+            }
+        }
+    }
+    let res = new HTML('h3', "Found %u projects".format(num));
+    wrapper.inject(res);
+    wrapper.inject(obj);
+    location.hash = name;
+}
+
+function render_projects(state, json) {
+    pjson = json;
+    let hash = location.hash.substr(1);
+    if (hash && hash.length > 0) {
+        document.getElementById('pkey').value = hash;
+        find_project(hash);
+        return;
+    }
+    let wrapper = document.getElementById('wrapper');
+    let obj = new HTML('div');
+    wrapper.innerHTML = "";
+    let keys = [];
+    let num = 0;
+    for (var key in json) { keys.push(key); }
+    keys.sort();
+    for (var i = 0; i < keys.length; i++) {
+        let key = keys[i];
+        project = json[key];
+        if (!project) continue;
+        if (project.has_default || project.images.length > 0) {
+            num++;
+            let div = make_div(key, project);
+            obj.inject(div);
+        }
+    }
+    let res = new HTML('h3', "Found %u projects".format(num));
+    wrapper.inject(res);
+    wrapper.inject(obj);
+}
+
+function init_projects() {
+    GET('res/logos.json?' + Math.random(), render_projects);
+}
+

Added: comdev/project-logos/site/js/scaffolding.js
URL: http://svn.apache.org/viewvc/comdev/project-logos/site/js/scaffolding.js?rev=1839221&view=auto
==============================================================================
--- comdev/project-logos/site/js/scaffolding.js (added)
+++ comdev/project-logos/site/js/scaffolding.js Sun Aug 26 14:42:11 2018
@@ -0,0 +1,191 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+/**
+ * HTML: DOM creator class
+ * args:
+ * - type: HTML element type (div, table, p etc) to produce
+ * - params: hash of element params to add (class, style etc)
+ * - children: optional child or children objects to insert into the new element
+ * Example:
+ * div = new HTML('div', {
+ *    class: "footer",
+ *    style: {
+ *        fontWeight: "bold"
+ *    }
+#}, "Some text inside a div")
+ */
+
+var txt = (msg) => document.createTextNode(msg);
+
+var HTML = (function() {
+  function HTML(type, params, children) {
+
+    /* create the raw element, or clone if passed an existing element */
+    var child, j, len, val;
+    if (typeof type === 'object') {
+      this.element = type.cloneNode();
+    } else {
+      this.element = document.createElement(type);
+    }
+
+    /* If params have been passed, set them */
+    if (isHash(params)) {
+      for (var key in params) {
+        val = params[key];
+
+        /* Standard string value? */
+        if (typeof val === "string" || typeof val === 'number') {
+          this.element.setAttribute(key, val);
+        } else if (isArray(val)) {
+
+          /* Are we passing a list of data to set? concatenate then */
+          this.element.setAttribute(key, val.join(" "));
+        } else if (isHash(val)) {
+
+          /* Are we trying to set multiple sub elements, like a style? */
+          for (var subkey in val) {
+            let subval = val[subkey];
+            if (!this.element[key]) {
+              throw "No such attribute, " + key + "!";
+            }
+            this.element[key][subkey] = subval;
+          }
+        }
+      }
+    } else {
+      if (!children) { children = params } // shortcut!
+    }
+
+    /* If any children have been passed, add them to the element */
+    if (children) {
+
+      /* If string, convert to textNode using txt() */
+      if (typeof children === "string") {
+        this.element.inject(txt(children));
+      } else {
+
+        /* If children is an array of elems, iterate and add */
+        if (isArray(children)) {
+          for (j = 0, len = children.length; j < len; j++) {
+            child = children[j];
+
+            /* String? Convert via txt() then */
+            if (typeof child === "string") {
+              this.element.inject(txt(child));
+            } else {
+
+              /* Plain element, add normally */
+              this.element.inject(child);
+            }
+          }
+        } else {
+
+          /* Just a single element, add it */
+          this.element.inject(children);
+        }
+      }
+    }
+    return this.element;
+  }
+
+  return HTML;
+
+})();
+
+/**
+ * prototype injector for HTML elements:
+ * Example: mydiv.inject(otherdiv)
+ */
+
+HTMLElement.prototype.inject = function(child) {
+  var item, j, len;
+  if (isArray(child)) {
+    for (j = 0, len = child.length; j < len; j++) {
+      item = child[j];
+      if (typeof item === 'string') {
+        item = txt(item);
+      }
+      this.appendChild(item);
+    }
+  } else {
+    if (typeof child === 'string') {
+      child = txt(child);
+    }
+    this.appendChild(child);
+  }
+  return child;
+};
+
+
+
+/**
+ * prototype for emptying an html element
+ */
+
+HTMLElement.prototype.empty = function() {
+  var ndiv;
+  ndiv = this.cloneNode();
+  this.parentNode.replaceChild(ndiv, this);
+  return ndiv;
+};
+
+function toggleView(id) {
+  let obj = document.getElementById(id);
+  if (obj) {
+    obj.style.display = (obj.style.display == 'block') ? 'none' : 'block';
+  }
+}
+
+function br() {
+  return new HTML('br');
+}
+
+// construction shortcuts for various elements
+let _a = (a,b) => new HTML('a', a,b);
+let _b = (a,b) => new HTML('b', a,b);
+let _p = (a,b) => new HTML('p', a,b);
+let _i = (a,b) => new HTML('i', a, b);
+let _div = (a,b) => new HTML('div', a, b);
+let _input = (a,b) => new HTML('input', a, b);
+let _select = (a,b) => new HTML('select', a, b);
+let _option = (a,b) => new HTML('option', a, b);
+let _h1 = (a,b) => new HTML('h1', a, b);
+let _h2 = (a,b) => new HTML('h2', a, b);
+let _h3 = (a,b) => new HTML('h3', a, b);
+let _h4 = (a,b) => new HTML('h4', a, b);
+let _h5 = (a,b) => new HTML('h5', a, b);
+let _kbd = (a,b) => new HTML('kbd', a, b);
+let _pre = (a,b) => new HTML('pre', a, b);
+let _hr = (a,b) => new HTML('hr', a, b);
+let _span = (a,b) => new HTML('span', a, b);
+let _textarea = (a,b) => new HTML('textarea', a, b);
+let _get = (a) => document.getElementById(a);
+
+
+function billitem(a,b,c, total) {
+  let d = _div({style: {position: 'relative', display: 'block'}});
+  if (total) { d.style.fontWeight = 'bold'; d.style.borderTop = '2px solid #444'; }
+  let da = _div({style: {position: 'relative', width: '600px', display: 'inline-block'}}, a);
+  let db = _div({style: {position: 'relative', width: '60px', display: 'inline-block'}}, b);
+  let dc = _div({style: {position: 'relative', width: '80px', textAlign: 'right', display: 'inline-block'}}, c);
+  d.inject(da);
+  d.inject(db);
+  d.inject(dc);
+  return d;
+}

Added: comdev/project-logos/site/tools/compile.py
URL: http://svn.apache.org/viewvc/comdev/project-logos/site/tools/compile.py?rev=1839221&view=auto
==============================================================================
--- comdev/project-logos/site/tools/compile.py (added)
+++ comdev/project-logos/site/tools/compile.py Sun Aug 26 14:42:11 2018
@@ -0,0 +1,207 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import subprocess
+import json
+import requests
+import re
+from PIL import Image
+import time
+
+# Standard in/out dirs
+img_dir = '/svn/asf-logos'
+out_dir = '/svn/logofinder/res'
+
+if len(sys.argv) > 2:
+    img_dir = sys.argv[1]
+    out_dir = sys.argv[2]
+
+
+# svn up before processing
+subprocess.check_output(('/usr/bin/svn', 'up', img_dir))
+
+# Gather all images to process
+files = [x for x in os.listdir(img_dir) if os.path.isfile(os.path.join(img_dir, x))]
+
+# Get all projects, committees, podlings
+cmts = requests.get('https://whimsy.apache.org/public/committee-info.json').json()
+pods = requests.get('https://whimsy.apache.org/public/public_podlings.json').json()
+projs = requests.get('https://projects.apache.org/json/foundation/projects.json').json()
+
+
+# Churn out a big json blob of projects
+projects = {}
+for k, cmt in cmts['committees'].items():
+    k = k.replace('-', '')
+    if k == 'httpcomponents':
+      k = 'hc'
+    el = {
+        'name': cmt['display_name'],
+        'established': cmt['established'],
+        'description': cmt['description'],
+        'website': cmt['site'],
+        'podling': False,
+        'has_default': False,
+        'images': []
+    }
+    projects[k] = el
+
+for k, cmt in pods['podling'].items():
+    k = k.replace('-', '')
+    el = {
+        'name': cmt['name'],
+        'established': cmt['startdate'],
+        'description': cmt['description'],
+        'website': "https://%s.apache.org/" % k,
+        'podling': True,
+        'has_default': False,
+        'images': []
+    }
+    if cmt['status'] == "current" and k not in projects:
+        projects[k] = el
+
+for k, cmt in projs.items():
+    k = k.replace('-', '')
+    el = {
+        'name': cmt['name'].replace('Apache ', ''),
+        'established': cmt.get('created', 'Sub project of %s' % cmt['pmc']),
+        'description': cmt.get('description', 'No description available'),
+        'website': cmt.get('homepage', 'https://www.apache.org/'),
+        'podling': False,
+        'has_default': False,
+        'images': []
+    }
+    if k not in projects:
+        k = re.sub(r"_.+", "", k)
+        projects[k] = el
+
+
+# Add foundation as a project
+projects['foundation'] = {
+  'name': 'Software Foundation',
+  'established': '1999-03',
+  'description': 'Foundation logo',
+  'website': "https://www.apache.org/",
+  'podling': False,
+  'has_default': False,
+  'images': []
+}
+
+# Prep montage for execution later
+montage = ['/usr/bin/montage', '-geometry', '200x150+4+4', '-tile', '20x']
+montage_tlps = ['/usr/bin/montage', '-geometry', '200x150+4+4', '-tile', '20x']
+montage_large = ['/usr/bin/montage', '-geometry', '1200x600+20+20', '-tile', '8x']
+montage_wide = ['/usr/bin/montage', '-geometry', '1200x600+20+20', '-tile', '15x']
+
+
+# Process each file...
+for file in files:
+  m = re.match(r"^([^.-]+).*?\.(.+)$", file)
+  project = m.group(1)
+  if project not in projects:
+    for k in projects:
+      if k.endswith("-%s" % project):
+        project = k
+        break
+  ext = m.group(2)
+  oext = ext
+  fpath = '%s/%s' % (img_dir, file)
+  st = os.stat(fpath)
+  skipit = False
+  if st.st_mtime < (time.time() - 4200) and os.path.exists('%s/%s/default.png' % (out_dir, project)):
+    print("Skipping %s" % fpath)
+    skipit = True
+  # EPS should be converted to PDF first, or inkscape borks :/
+  if ext == 'eps':
+      xfile = file.replace('.'+ext, '')
+      if not skipit and not os.path.exists('%s/%s.pdf' % (img_dir, xfile)):
+        subprocess.check_output(('ps2pdf', '-dPDFSETTINGS=/prepress', '-dEPSCrop', '%s/%s' % (img_dir, file), '%s/pdftmp/%s.pdf' % (img_dir, xfile)))
+      ext = 'pdf'
+      file = 'pdftmp/' + xfile + '.pdf'
+  # Scalable formats
+  if ext in ['svg', 'ai', 'pdf', 'png']:
+      print("Converting %s to PNG/PDF" % file)
+      xfile = file.replace('.'+ext, '').replace('pdftmp/', '')
+      ppath = "%s/%s" % (out_dir, project)
+      if not os.path.isdir(ppath):
+          os.mkdir(ppath)
+      try:
+          if xfile == project or xfile == '%s-1' % project:
+              if not skipit:
+                subprocess.check_output(('inkscape', '-z', '%s/%s' % (img_dir, file), '-e', '%s/%s/default.png' % (out_dir, project), '-w', '720'))
+              projects[project]['has_default'] = True
+      except:
+          pass
+
+      # PNG, normal res
+      if xfile == project or xfile == '%s-1' % project:
+        if project != 'foundation':
+          montage_large.append('%s/%s/%s.png' % (out_dir, project, xfile))
+          montage_wide.append('%s/%s/%s.png' % (out_dir, project, xfile))
+      try:
+          if not skipit:
+            subprocess.check_output(('inkscape', '-z', '%s/%s' % (img_dir, file), '-e', '%s/%s/%s.png' % (out_dir, project, xfile), '-w', '1200'))
+          im = Image.open('%s/%s/%s.png' % (out_dir, project, xfile))
+          w = im.width
+          h = im.height
+          projects[project]['images'].append({'original': xfile, 'name': '%s' % xfile, 'filename': '%s/%s.png' % (project, xfile), 'format': 'png', 'width': w, 'height': h})
+      except:
+          pass
+      # PNG, high res
+      try:
+          if not skipit:
+            subprocess.check_output(('inkscape', '-z', '%s/%s' % (img_dir, file), '-e', '%s/%s/%s_highres.png' % (out_dir, project, xfile), '-w', '3600'))
+          im = Image.open('%s/%s/%s_highres.png' % (out_dir, project, xfile))
+          w = im.width
+          h = im.height
+          projects[project]['images'].append({'original': xfile, 'name': '%s (high res)' % xfile, 'filename': '%s/%s_highres.png' % (project, xfile), 'format': 'png', 'width': w, 'height': h})
+      except:
+          pass
+      try:
+          if not skipit:
+            subprocess.check_output(('inkscape', '-z', '%s/%s' % (img_dir, file), '-A', '%s/%s/%s.pdf' % (out_dir, project, xfile), '-w', '1200'))
+          projects[project]['images'].append({'original': xfile, 'name': xfile, 'filename': '%s/%s.pdf' % (project, xfile), 'format': 'pdf'})
+      except:
+          pass
+
+with open("%s/logos.json" % out_dir, "w") as f:
+    json.dump(projects, f, indent = 2)
+
+
+
+for project in projects:
+  xp =  '%s/%s/default.png' % (out_dir, project)
+  if os.path.exists(xp):
+    if project != 'foundation':
+      montage.append(xp)
+      if not projects[project]['podling']:
+        montage_tlps.append(xp)
+
+montage.append('%s../montages/standard.png' % out_dir)
+montage_tlps.append('%s../montages/standard_tlps_only.png' % out_dir)
+montage_large.append('%s../montages/highres_tall.png' % out_dir)
+montage_wide.append('%s../montages/highres_wide.png' % out_dir)
+
+try:
+  st = os.stat('%s../montages/standard.png' % out_dir)
+except:
+  st = None
+if (not st) or st.st_mtime < (time.time() - 43200):
+  print("Making montage...")
+  subprocess.check_output(montage)
+  subprocess.check_output(montage_tlps)
+  print("Making montage (high res, tall)...")
+  subprocess.check_output(montage_large)
+  print("Making montage (high res, wide)...")
+  subprocess.check_output(montage_wide)
+else:
+  print("Skipping montages...")
+
+print("The following TLPs are currently not represented: ")
+for k, cmt in cmts['committees'].items():
+  k = k.replace('-', '')
+  if not projects.get(k):
+    continue
+  if len(projects[k]['images']) == 0:
+    print(k)

Added: comdev/project-logos/tools/compile.py
URL: http://svn.apache.org/viewvc/comdev/project-logos/tools/compile.py?rev=1839221&view=auto
==============================================================================
--- comdev/project-logos/tools/compile.py (added)
+++ comdev/project-logos/tools/compile.py Sun Aug 26 14:42:11 2018
@@ -0,0 +1,208 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import subprocess
+import json
+import requests
+import re
+from PIL import Image
+import time
+
+# Standard in/out dirs
+img_dir = '/svn/project-logos/originals'
+out_dir = '/var/www/html/logos/res'
+
+if len(sys.argv) > 2:
+    img_dir = sys.argv[1]
+    out_dir = sys.argv[2]
+
+
+# svn up before processing
+subprocess.check_output(('/usr/bin/svn', 'up', img_dir))
+
+# Gather all images to process
+files = [x for x in os.listdir(img_dir) if os.path.isfile(os.path.join(img_dir, x))]
+
+# Get all projects, committees, podlings
+cmts = requests.get('https://whimsy.apache.org/public/committee-info.json').json()
+pods = requests.get('https://whimsy.apache.org/public/public_podlings.json').json()
+projs = requests.get('https://projects.apache.org/json/foundation/projects.json').json()
+
+
+# Churn out a big json blob of projects
+projects = {}
+for k, cmt in cmts['committees'].items():
+    k = k.replace('-', '')
+    if k == 'httpcomponents':
+      k = 'hc'
+    el = {
+        'name': cmt['display_name'],
+        'established': cmt['established'],
+        'description': cmt['description'],
+        'website': cmt['site'],
+        'podling': False,
+        'has_default': False,
+        'images': []
+    }
+    projects[k] = el
+
+for k, cmt in pods['podling'].items():
+    k = k.replace('-', '')
+    el = {
+        'name': cmt['name'],
+        'established': cmt['startdate'],
+        'description': cmt['description'],
+        'website': "https://%s.apache.org/" % k,
+        'podling': True,
+        'has_default': False,
+        'images': []
+    }
+    if cmt['status'] == "current" and k not in projects:
+        projects[k] = el
+
+for k, cmt in projs.items():
+    k = k.replace('-', '')
+    el = {
+        'name': cmt['name'].replace('Apache ', ''),
+        'established': cmt.get('created', 'Sub project of %s' % cmt['pmc']),
+        'description': cmt.get('description', 'No description available'),
+        'website': cmt.get('homepage', 'https://www.apache.org/'),
+        'podling': False,
+        'has_default': False,
+        'images': []
+    }
+    if k not in projects:
+        k = re.sub(r"_.+", "", k)
+        projects[k] = el
+
+
+# Add foundation as a project
+projects['foundation'] = {
+  'name': 'Software Foundation',
+  'established': '1999-03',
+  'description': 'Foundation logo',
+  'website': "https://www.apache.org/",
+  'podling': False,
+  'has_default': False,
+  'images': []
+}
+
+# Prep montage for execution later
+montage = ['/usr/bin/montage', '-geometry', '200x150+4+4', '-tile', '20x']
+montage_tlps = ['/usr/bin/montage', '-geometry', '200x150+4+4', '-tile', '20x']
+montage_large = ['/usr/bin/montage', '-geometry', '1200x600+20+20', '-tile', '8x']
+montage_wide = ['/usr/bin/montage', '-geometry', '1200x600+20+20', '-tile', '15x']
+
+
+# Process each file...
+for file in files:
+  m = re.match(r"^([^.-]+).*?\.(.+)$", file)
+  project = m.group(1)
+  if project not in projects:
+    for k in projects:
+      if k.endswith("-%s" % project):
+        project = k
+        break
+  ext = m.group(2)
+  oext = ext
+  fpath = '%s/%s' % (img_dir, file)
+  st = os.stat(fpath)
+  skipit = False
+  if st.st_mtime < (time.time() - 4200) and os.path.exists('%s/%s/default.png' % (out_dir, project)):
+    print("Skipping %s" % fpath)
+    skipit = True
+  # EPS should be converted to PDF first, or inkscape borks :/
+  if ext == 'eps':
+      xfile = file.replace('.'+ext, '')
+      if not skipit and not os.path.exists('%s/%s.pdf' % (img_dir, xfile)):
+        subprocess.check_output(('ps2pdf', '-dPDFSETTINGS=/prepress', '-dEPSCrop', '%s/%s' % (img_dir, file), '%s/pdftmp/%s.pdf' % (img_dir, xfile)))
+      ext = 'pdf'
+      file = 'pdftmp/' + xfile + '.pdf'
+  # Scalable formats
+  if ext in ['svg', 'ai', 'pdf', 'png']:
+      print("Converting %s to PNG/PDF" % file)
+      xfile = file.replace('.'+ext, '').replace('pdftmp/', '')
+      ppath = "%s/%s" % (out_dir, project)
+      if not os.path.isdir(ppath):
+          os.mkdir(ppath)
+      try:
+          if xfile == project or xfile == '%s-1' % project:
+              if not skipit:
+                subprocess.check_output(('inkscape', '-z', '%s/%s' % (img_dir, file), '-e', '%s/%s/default.png' % (out_dir, project), '-w', '720'))
+              projects[project]['has_default'] = True
+      except:
+          pass
+
+      # PNG, normal res
+      if xfile == project or xfile == '%s-1' % project:
+        if project != 'foundation':
+          montage_large.append('%s/%s/%s.png' % (out_dir, project, xfile))
+          montage_wide.append('%s/%s/%s.png' % (out_dir, project, xfile))
+      try:
+          if not skipit:
+            subprocess.check_output(('inkscape', '-z', '%s/%s' % (img_dir, file), '-e', '%s/%s/%s.png' % (out_dir, project, xfile), '-w', '1200'))
+          im = Image.open('%s/%s/%s.png' % (out_dir, project, xfile))
+          w = im.width
+          h = im.height
+          projects[project]['images'].append({'original': xfile, 'name': '%s' % xfile, 'filename': '%s/%s.png' % (project, xfile), 'format': 'png', 'width': w, 'height': h})
+      except:
+          pass
+      # PNG, high res
+      try:
+          if not skipit:
+            subprocess.check_output(('inkscape', '-z', '%s/%s' % (img_dir, file), '-e', '%s/%s/%s_highres.png' % (out_dir, project, xfile), '-w', '3600'))
+          im = Image.open('%s/%s/%s_highres.png' % (out_dir, project, xfile))
+          w = im.width
+          h = im.height
+          projects[project]['images'].append({'original': xfile, 'name': '%s (high res)' % xfile, 'filename': '%s/%s_highres.png' % (project, xfile), 'format': 'png', 'width': w, 'height': h})
+      except:
+          pass
+      try:
+          if not skipit:
+            subprocess.check_output(('inkscape', '-z', '%s/%s' % (img_dir, file), '-A', '%s/%s/%s.pdf' % (out_dir, project, xfile), '-w', '1200'))
+          projects[project]['images'].append({'original': xfile, 'name': xfile, 'filename': '%s/%s.pdf' % (project, xfile), 'format': 'pdf'})
+      except:
+          pass
+
+with open("%s/logos.json" % out_dir, "w") as f:
+    json.dump(projects, f, indent = 2)
+
+
+
+for project in projects:
+  xp =  '%s/%s/default.png' % (out_dir, project)
+  if os.path.exists(xp):
+    if project != 'foundation':
+      montage.append(xp)
+      if not projects[project]['podling']:
+        montage_tlps.append(xp)
+
+montage.append('%s../montages/standard.png' % out_dir)
+montage_tlps.append('%s../montages/standard_tlps_only.png' % out_dir)
+montage_large.append('%s../montages/highres_tall.png' % out_dir)
+montage_wide.append('%s../montages/highres_wide.png' % out_dir)
+
+# Only make new montages every 12 hours
+try:
+  st = os.stat('%s../montages/standard.png' % out_dir)
+except:
+  st = None
+if (not st) or st.st_mtime < (time.time() - 43200):
+  print("Making montage...")
+  subprocess.check_output(montage)
+  subprocess.check_output(montage_tlps)
+  print("Making montage (high res, tall)...")
+  subprocess.check_output(montage_large)
+  print("Making montage (high res, wide)...")
+  subprocess.check_output(montage_wide)
+else:
+  print("Skipping montages...")
+
+print("The following TLPs are currently not represented: ")
+for k, cmt in cmts['committees'].items():
+  k = k.replace('-', '')
+  if not projects.get(k):
+    continue
+  if len(projects[k]['images']) == 0:
+    print(k)



Mime
View raw message