community-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From humbed...@apache.org
Subject svn commit: r1755684 - in /comdev/helpwanted.apache.org/site/js: coffee/ coffee/colors.coffee coffee/combine.sh coffee/hw.coffee coffee/util.coffee hw.js
Date Wed, 10 Aug 2016 07:50:59 GMT
Author: humbedooh
Date: Wed Aug 10 07:50:59 2016
New Revision: 1755684

URL: http://svn.apache.org/viewvc?rev=1755684&view=rev
Log:
Rewrite the HW frontend a bit. More work to be done, but it's a start.

Added:
    comdev/helpwanted.apache.org/site/js/coffee/
    comdev/helpwanted.apache.org/site/js/coffee/colors.coffee
    comdev/helpwanted.apache.org/site/js/coffee/combine.sh
    comdev/helpwanted.apache.org/site/js/coffee/hw.coffee
    comdev/helpwanted.apache.org/site/js/coffee/util.coffee
Modified:
    comdev/helpwanted.apache.org/site/js/hw.js

Added: comdev/helpwanted.apache.org/site/js/coffee/colors.coffee
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/js/coffee/colors.coffee?rev=1755684&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/js/coffee/colors.coffee (added)
+++ comdev/helpwanted.apache.org/site/js/coffee/colors.coffee Wed Aug 10 07:50:59 2016
@@ -0,0 +1,92 @@
+
+hsl2rgb = (h, s, l) ->
+    
+    h = h % 1;
+    s = 1 if s > 1
+    l = 1 if l > 1
+    if l <= 0.5
+        v = (l * (1 + s))
+    else
+        v = (l + s - l * s);
+    if v == 0
+        return {
+            r: 0,
+            g: 0,
+            b: 0
+        }
+
+    min = 2 * l - v;
+    sv = (v - min) / v;
+    sh = (6 * h) % 6;
+    switcher = Math.floor(sh);
+    fract = sh - switcher;
+    vsf = v * sv * fract;
+
+    switch switcher
+        when 0
+            return {
+                r: v,
+                g: min + vsf,
+                b: min
+            };
+        when 1
+            return {
+                r: v - vsf,
+                g: v,
+                b: min
+            };
+        when 2
+            return {
+                r: min,
+                g: v,
+                b: min + vsf
+            };
+        when 3
+            return {
+                r: min,
+                g: v - vsf,
+                b: v
+            };
+        when 4
+            return {
+                r: min + vsf,
+                g: min,
+                b: v
+            };
+        when 5
+            return {
+                r: v,
+                g: min,
+                b: v - vsf
+            };
+    
+    return {
+        r: 0,
+        g: 0,
+        b: 0
+    };
+
+
+genColors = (numColors, saturation, lightness, hex) ->
+    cls = []
+    baseHue = 1.34;
+    for i in [1..numColors]
+        c = hsl2rgb(baseHue, saturation, lightness)
+        if (hex) 
+            h = ( Math.round(c.r*255*255*255) + Math.round(c.g * 255*255) + Math.round(c.b*255) ).toString(16)
+            while h.length < 6
+                h = '0' + h
+            h = '#' + h
+            cls.push(h);
+        else
+                cls.push({
+                    r: parseInt(c.r * 255),
+                    g: parseInt(c.g * 255),
+                    b: parseInt(c.b * 255)
+                })
+        
+        baseHue -= 0.23
+        if (baseHue < 0) 
+            baseHue += 1
+    
+    return cls

Added: comdev/helpwanted.apache.org/site/js/coffee/combine.sh
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/js/coffee/combine.sh?rev=1755684&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/js/coffee/combine.sh (added)
+++ comdev/helpwanted.apache.org/site/js/coffee/combine.sh Wed Aug 10 07:50:59 2016
@@ -0,0 +1,3 @@
+#!/bin/bash
+coffee -b --join ../hw.js -c *.coffee
+

Added: comdev/helpwanted.apache.org/site/js/coffee/hw.coffee
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/js/coffee/hw.coffee?rev=1755684&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/js/coffee/hw.coffee (added)
+++ comdev/helpwanted.apache.org/site/js/coffee/hw.coffee Wed Aug 10 07:50:59 2016
@@ -0,0 +1,463 @@
+###
+ 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.
+###
+
+hw_weburl = new RegExp(
+  "(" +
+    # protocol identifier
+    "(?:(?:https?|ftp)://)" +
+    # user:pass authentication
+    "(?:\\S+(?::\\S*)?@)?" +
+    "(?:" +
+      # IP address exclusion
+      # private & local networks
+      "(?!(?:10|127)(?:\\.\\d{1,3}){3})" +
+      "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +
+      "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" +
+      # IP address dotted notation octets
+      # excludes loopback network 0.0.0.0
+      # excludes reserved space >= 224.0.0.0
+      # excludes network & broacast addresses
+      # (first & last IP address of each class)
+      "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
+      "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
+      "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
+    "|" +
+      # host name
+      "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
+      # domain name
+      "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" +
+      # TLD identifier
+      "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" +
+      # TLD may end with dot
+      "\\.?" +
+    ")" +
+    # port number
+    "(?::\\d{2,5})?" +
+    # resource path
+    "(?:[/?#]([^,<>()\\[\\] \\t\\r\\n]|(<[^:\\s]*?>|\\([^:\\s]*?\\)|\\[[^:\\s]*?\\]))*)?" +
+    ")\\.?"
+  , "mig"
+);
+
+
+pastels = genColors(16, 0.9, 0.85)
+brights = genColors(16, 0.8, 0.675)
+
+splashOptions = {
+    'I want to help out!': 'this.parentNode.style.display="none"; wizard(1);',
+    'I am an Apache committer and wish to add or edit tasks.': 'location.href="/admin/";',
+    'I want to help spread the word about "Help Wanted"!': 'location.href="wtest.html";',
+}
+
+
+# Difficulty levels
+diff = ['Beginner', 'Journeyman', 'Intermediate', 'Advanced', 'Expert']
+diff_explanation = [
+    'This is an easy task that anyone can get started on',
+    'This requires a bit of knowledge of the project, but otherwise is an easy task',
+    'This requires a good knowledge of the project',
+    'This requires a good knowledge of the project and good technical skills',
+    'This requires intimate knowledge of the project and excellent technical skills'
+]
+
+# Languages (programming + spoken)
+langs = ['c', 'xml', 'c++', 'c-sharp', 'java', 'javascript', 'css', 'html', 'perl', 'ruby', 'lua', 'python', 'go', 'rust', 'erlang', 'swift', 'groovy', 'haskell', 'scala', 'php', 'bash', 'tcl', 'jsp', 'svg']
+spoken_langs = ['english', 'french', 'german', 'spanish', 'russian', 'italian', 'japanese', 'chinese']
+website_langs = ['css','javascript','html']
+
+# Task categories
+types = {
+    programming: "Programming and Development"
+    'web design': "Web Design"
+    marketing: 'Marketing and Publicity'
+    documentation: 'Documentation and Guides'
+    community: 'Community Outreach'
+    translation: 'Translation'
+}
+
+
+# TBD
+projects = ['all projects']
+cjson = {}
+
+# Default item limit
+max_items = 10
+
+# Sort some things
+langs.sort()
+
+showSplash = (args) ->
+    obj = get('splash')
+    get('pickerparent').style.display = "none"
+    get('wizard_step1').style.display = "none"
+    obj.style.display = "block"
+    obj.innerHTML = ""
+    app(obj, mk('span', { style: "font-size: 20pt; font-family: sans-serif; color: #FFF;"}, "What would you like to do today?"))
+    i = 0
+    for title, oc of splashOptions
+        p = pastels[i]
+        b = brights[i]
+        d = mk('div', { class: "option", onclick: oc, style: "background: rgba(" + p.r + "," + p.g + "," + p.b + ", 0.85);"})
+        btn = mk('div', {style: "background: rgba(" + b.r + "," + b.g + "," + b.b + ", 1);"})
+        app(d, btn)
+        app(d, title)
+        app(obj, d)
+        i++
+
+wstate = {}
+
+wizard = (step, arg) ->
+    obj = get('innerpicker')
+    pobj = get('pickerparent')
+    sobj = get('splash')
+    wobj = get('wizard_step1')
+    if step == 0
+        pobj.style.display = "none"
+        wobj.style.display = "none"
+        sobj.style.display = "block"
+    else if step > 1
+        pobj.style.display = "block"
+        
+    get('hwitems').innerHTML = ""
+    if not step or step == 0
+        step = 1
+    
+    if step == 1
+        wstate = {}
+        wobj.style.display = "block"
+        wcobj = get('wizard_step1_contents')
+        wcobj.innerHTML = ""
+        app(wcobj, mk('span', {style:'font-size: 20pt; font-family: sans-serif; color: #FFF;'}, "What kind of tasks would you like to help with?"))
+        i = 0
+        for type, desc of types
+            p = pastels[i]
+            b = brights[i]
+            d = mk('div', {class: "option", onclick:"wizard(2, '" + type + "');", style: "background: rgba(" + p.r + "," + p.g + "," + p.b + ", 0.85);"})
+            btn = mk('img', { src: "images/" + type.replace(/\s+/g, "") + "_large.png", style: "width: 36px; heigh: 36px; vertical-align: middle; padding: 4px; padding-right: 12px;"})
+            app(d, btn)
+            app(d, desc)
+            app(wcobj, d)
+            i++;
+        
+        
+        # show me everything
+        p = pastels[i]
+        b = brights[i]
+        d = mk('div', {class: "option", onclick: "fetchItems();", style: "background:rgba(" + p.r + "," + p.g + "," + p.b + ", 0.85);"})
+        btn = mk('img', {src: "images/everything_large.png", style: "width: 36px; heigh: 36px; vertical-align: middle; padding: 4px; padding-right: 12px;"})
+        app(d, btn)
+        app(d, "Just show me everything...")
+        app(wcobj, d)
+        
+    if step == 2 and arg
+        wstate = {
+            type: arg
+        }
+        desc = types[arg]
+        wcobj = get('wizard_step1_contents')
+        wcobj.innerHTML = ""
+        app(wcobj, mk('span', {style:'font-size: 20pt; font-family: sans-serif; color: #FFF;'}, desc))
+        obj = mk('div', {class: "optiontwo"})
+        app(wcobj, obj)
+        i = 0
+        for type, desc of types
+            if type == arg
+                break
+            i++;
+        
+        p = pastels[i]
+        b = brights[i]
+        obj.style.background = "rgba(" + p.r + "," + p.g + "," + p.b + ", 0.95)"
+        obj.style.color = "#333 !important"
+        obj.style.height = "340px"
+        
+        app(obj, mk('h2', {style: "margin-bottom: 4px; line-height: 18pt; text-align: center;"}, "Which languages are you proficient in?"))
+        app(obj, mk('p', {}, "You don't need to pick a language, but it will help narrow down the tasks available for you."))
+        
+        if arg in ['programming', 'documentation']
+            for lang, i in langs
+                div = mk('div', { style: "float: left; width: 100px; height: 28px;"})
+                cb = mk('input', { type: "checkbox", id:  "plang_" + lang })
+                lbl = mk('label', {for: "plang_" + lang}, lang)
+                app(div, cb)
+                app(div, lbl)
+                app(obj, div)
+            app(obj, mk('br'))
+        else if arg == 'web design'
+            for lang, i in website_langs
+                div = mk('div', { style: "float: left; width: 100px; height: 28px;"})
+                cb = mk('input', { type: "checkbox", id:  "plang_" + lang })
+                lbl = mk('label', {for: "plang_" + lang}, lang)
+                app(div, cb)
+                app(div, lbl)
+                app(obj, div)
+            app(obj, mk('br'))
+        else
+            for lang, i in spoken_langs
+                div = mk('div', { style: "float: left; width: 100px; height: 28px;"})
+                cb = mk('input', { type: "checkbox", id:  "plang_" + lang })
+                lbl = mk('label', {for: "plang_" + lang}, lang.replace(/^([a-z])/, (a) => a.toUpperCase() ))
+                app(div, cb)
+                app(div, lbl)
+                app(obj, div)
+            app(obj, mk('br'))
+
+        fdiv = mk('div', { style: "height: 60px; width: 100%; margin-top: 30px; float: left;"})
+        fbtn = mk('input', { type: "button", class: "finishbutton", onclick: "doForm(this.parentNode.parentNode);", value: "Find me something to do!"})
+        fspan = mk('span', { style: "font-size: 14pt; color: #333;"}, "◀")
+        app(fspan, mk('a', {style:"color: #333;", onclick:"wizard(1)", href:"javascript:void(0);"}, "Back to start"))
+        app(fdiv, [fbtn, fspan])
+        app(obj, fdiv)
+
+populateForm = () ->
+    # languages
+    f = get('plang')
+    for lang in langs
+        div = mk('div', { style: "float: left; width: 100px;" })
+        cb = mk('input', { type: "checkbox", id: "plang_" + lang})
+        lbl = mk('label', { for: "plang_" + lang}, lang)
+        app(div, [cb, lbl])
+        app(f, div)
+    app(f, mk('br'))
+    
+    # task types
+    f = get('ptypes')
+    for type in types
+        div = mk('div', { style: "float: left; width: 200px;" })
+        cb = mk('input', { type: "checkbox", id: "ptype__" + type})
+        lbl = mk('label', { for: "ptype_" + type}, type)
+        app(div, [cb, lbl])
+        app(f, div)
+    app(f, mk('br'))
+    
+    
+    # projects - none for now
+    f = get('pprojects')
+    app(f, "All projects")
+
+
+doForm = (par) ->
+    l = []
+    t = []
+    p = []
+    for lang in langs
+        if get("plang_" + lang) and get("plang_" + lang).checked
+            l.push(lang)
+    for type in types
+        if get("ptype_" + type) and get("ptype" + type).checked
+            t.push(type)
+    if wstate.type
+        t = [wstate.type]
+    
+    if wstate.projects
+        p = wstate.projects
+    fetchItems(l, t, p, null, par)
+
+sw = (id) ->
+    obj = get(id)
+    obj.style.display = (if obj.style.display == 'none' then 'table-row' else 'none')
+
+
+reallyPopulate = (json, state) ->
+    pro = []
+    obj = get('project')
+    
+    # optgroup for spoken/written
+    optg = mk('optgroup', { label: (if state then 'Non-TLPs:' else 'Top Level Projects:')})
+    app(obj, optg)
+    
+    for group in (json.committees || json.groups)
+        pro.push(group)
+        app(obj, mk('option', { value: group}, group ))
+    if state
+        return
+    
+    obj = get('languages')
+    # optgroup for programming
+    optg = mk('optgroup', { label: "Programming languages:"})
+    app(obj, optg)
+    
+    # prog languages
+    for lang in langs
+        app(obj, mk('option', { text: lang, value: lang}, lang ))
+    
+    # optgroup for spoken/written
+    optg = mk('optgroup', { label: "Spoken languages:"})
+    app(obj, optg)
+    
+    for lang in spoken_langs
+        app(obj, mk('option', {value: lang}, lang.replace(/^([a-z])/, (a) => a.toUpperCase()) ))
+        opt = document.createElement('option')
+    if not state or state == 0
+        fetch('https://whimsy.apache.org/public/public_nonldap_groups.json', 'other', reallyPopulate)
+
+populateAdminForm = () ->
+    fetch('https://whimsy.apache.org/public/public_ldap_committees.json', false, reallyPopulate)   
+
+
+displayItems = (json, state) ->
+    json = if json then json.tasks else cjson
+    cjson = json
+    numItems = 0
+    for item in json
+        if item.closed
+            continue
+        numItems++
+    
+    if numItems == 0 and state and state.parentObject
+        state.parentObject.style.display = 'none'
+        window.setTimeout(
+          () ->
+            state.parentObject.style.display = 'block'
+          , 50)
+        state.parentObject.innerHTML =""
+        div = mk('div', { style: "line-height: 12pt; text-align: center;"}, [
+          mk('h2', {}, "Dang it!"),
+          mk('p', {}, [
+            "Sorry, we couldn't find any open tasks matching your criteria.",
+            mk('br'),
+            "You could try expanding your search or picking a different category.",
+            mk('br'),
+            mk('br')
+            ]),
+          mk('p', {}, [
+            mk('span', { style:"font-size: 14pt; color: #333;"}, [
+              "◀",
+              mk('a', { style:"color: #333;", onclick:'wizard(1)', href:'javascript:void(0);'}, "Back to start")
+              ])
+            ])
+          ])
+          
+        app(state.parentObject, div)
+        return
+    
+    if state and typeof(state) == "string"
+        if (hw_oldstate == state)
+            json.sort((a,b) => a[state] < b[state])
+            hw_oldstate = ""
+        else
+            json.sort((a,b) => a[state] > b[state])
+            hw_oldstate = state
+    
+    obj = get('hwitems')
+    
+    if (typeof(numItems) == 'undefined')
+        obj.innerHTML = "Sorry, we couldn't find any tasks matching your criteria"
+        return
+    
+    
+    obj.innerHTML = "<p id='hwrtable'>Found " + numItems + " item" + (if numItems != 1 then "s" else "") + " you might be interested in:</p>"
+    colors = genColors(numItems+2)
+    
+    admintab = ""
+    if state and state.admin
+        admintab = "<th style='width: 80px;'>Actions</th>"
+    
+    tbl = "<table style='text-align: left; width: 100%;'>" +
+    "<tr style='cursor: pointer' title='Click on a column to sort'><th>&nbsp;</th><th onclick='displayItems(null, \"project\");'>Project</th>" +
+    "<th onclick='displayItems(null, \"title\");'>Title</th>" +
+    "<th onclick='displayItems(null, \"languages\");'>Languages</th>" +
+    "<th onclick='displayItems(null, \"difficulty\");'>Difficulty</th>" +
+    "<th onclick='displayItems(null, \"created\");'>Created</th>" +
+    admintab + "</tr>"
+    for item, i in json
+        if item.closed
+            continue
+        
+        if i >= max_items
+            tbl += "<tr><td colspan='5'><a href='javascript:void(0);' onclick='max_items += 10; displayItems(null, " + JSON.stringify(state||{}) + ");'>Show more tasks</a></td></tr>"
+            break
+        
+        z = i+1
+        ptype = item.type.replace(/\s+/g, "")
+        cdate = new Date(item.created*1000).toLocaleString()
+        lingos = (if item.languages != 'n/a' then item.languages.split(",").join(", ") else "")
+        
+        # admin stuff
+        add = ""
+        if state and state.admin
+            add = " <td><a href='/admin/close.lua?id=" + item.request_id + "'>Mark as done</a></td>"
+        
+        item.description = item.description.replace(/\n/g, "<br/>").replace(hw_weburl, (a) => ("<a href='"+a+"'>"+a+"</a>"))
+        tbl += "<tr style='cursor: pointer;' onclick=\"sw('details_" + i + "');\"><td width='68'><div class='itemNumber-yellow'>" + z + "</div><img title='" + item.type + "' style='float: left; width: 24px; height: 24px;' src='https://helpwanted.apache.org/images/icon_" + ptype + ".png'/></td>" +
+        "<td>" + item.project + "</td>"+
+        "<td style='text-align: left;'>" + item.title + "</td>" +
+        "<td>" + lingos + "</td><td title='" + diff_explanation[parseInt(item.difficulty)] + "' style='text-align: left;'><img style='width: 20px; height: 20px; vertical-align: middle;' src='https://helpwanted.apache.org/images/level_" + (parseInt(item.difficulty)+1) + ".png'/> " + diff[item.difficulty] + "</td><td>" + cdate + "</td>" + add + "</tr>"
+        
+        tbl += "<tr style='display:none;' id='details_" + i + "'><td colspan='6'><b>Project:</b> " + item.project + "<br/><b>Requested by:</b> " + item.author + "@apache.org<br/><b>Created:</b> " + cdate + "<br/><b>Description:</b> <blockquote>" + item.description + "</blockquote><b>Further information: </b> <a href='" + item.url + "'>" + item.url + "</a><br/><input type='button' onclick='location.href=\"https://helpwanted.apache.org/task.html?" + item.request_id + "\";' value='I am interested in this'/></td></tr>"
+     
+    tbl += "</table>"
+    obj.innerHTML += tbl
+    if location.href.search('admin') == -1
+        location.hash = '#hwrtable'
+    
+    
+fetchItems = (languages, types, projects, sortBy, par) ->
+    if not languages
+      languages = []
+    if not types
+      types = []
+    if not projects
+      projects = []
+    fetch("https://helpwanted.apache.org/tasks.lua?lang=" + languages.join(",") + "&type=" + types.join(",") + "&project=" + projects.join(","),
+                 {
+                    languages: languages,
+                    types: types,
+                    projects: projects,
+                    sortBy: sortBy,
+                    parentObject: par
+                    }, displayItems)
+
+
+fetchItemsAdmin = () ->
+    fetch("https://helpwanted.apache.org/tasks.lua", {admin: true}, displayItems)
+
+
+renderItem = (json, state) ->
+    p = pastels[parseInt(Math.random()*pastels.length)]
+    obj = get('item')
+    cdate = new Date(json.created*1000).toDateString()
+    rid = json.request_id.substring(0,8)
+    overrides = {
+        comdev: 'community',
+        whimsy: 'whimsical'
+    }
+    if (overrides[json.project])
+        json.project = overrides[json.project]
+    
+    json.description = json.description.replace(/\n/g, "<br/>").replace(hw_weburl, (a) => ("<a href='"+a+"'>"+a+"</a>"))
+    obj.innerHTML = "<h2>Task #" + state.substring(0,8) + ": " + json.title + "</h2>"
+    mlink = "mailto:dev@" + json.project + ".apache.org?subject=" + escape("Help with task: " + json.title) + "&body=" + escape("I would like to help out with the task listed at https://helpwanted.apache.org/task.html?" + rid + "\n\n")
+    rgba = "rgba(" + p.r + "," + p.g + "," + p.b + ", 1)"
+    obj.innerHTML += "<div id='pickerparent' style='background: " + rgba + "; padding:12px;'><p style='text-align: left;'><b>Project: </b> " + json.project + "<br/>" +
+        "<b>Created by:</b> " + json.author + "@apache.org<br/>" +
+        "<b>Task added: </b>" + cdate + "<br/>" +
+        "<b>Difficulty: </b> <img style='width: 16px; height: 16px; vertical-align: middle;' src='/images/level_" + (parseInt(json.difficulty)+1) + ".png'/> " + diff[json.difficulty] + " - " + diff_explanation[parseInt(json.difficulty)] + "<br/>" +
+        "<b>Task type:</b> " + types[json.type] + "<br/>" +
+        (if json.url and json.url.length > 10 then ("<b>Additional information:</b> <a href='" + json.url + "'>" + json.url + "</a><br/>") else "")+
+        (if json.estimate and json.estimate.length > 0 then ("<b>Estimated time to complete:</b> " + json.estimate) else "") +
+        (if json.timeout and json.timeout > 0 then ("<b>Task expires:</b> " + new Date(json.timeout*1000).toDateString()) else "") +
+        "<blockquote style='text-align: left;'><q>" + json.description + "</q></blockquote>" +
+        "<br/></p>" +
+        "<h3 style='text-align: left;'>How to help:</h3><p style='text-align: left;'>" +
+        (if json.curl and json.curl.length > 10 then ("<b>Contributor's guide for this project: </b><a href='" + json.curl + "'>" + json.curl + "</a><br/>") else "") +
+        "If you want to help with this task, please get in touch with the project at: <a href=\""+mlink+"\">dev@" + json.project + ".apache.org</a>!" +
+        "<br/>You should also check out the additional information URL (if such is provided above) for more information."
+        "<br/>&nbsp;<br/>&nbsp;<br/></p></div>"
+
+displayItem = (id) ->
+    fetch("https://helpwanted.apache.org/tasks.lua?id=" + id, id, renderItem)

Added: comdev/helpwanted.apache.org/site/js/coffee/util.coffee
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/js/coffee/util.coffee?rev=1755684&view=auto
==============================================================================
--- comdev/helpwanted.apache.org/site/js/coffee/util.coffee (added)
+++ comdev/helpwanted.apache.org/site/js/coffee/util.coffee Wed Aug 10 07:50:59 2016
@@ -0,0 +1,164 @@
+Number.prototype.pretty = (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,');
+
+
+fetch = (url, xstate, callback, snap) ->
+    xmlHttp = null;
+    # Set up request object
+    if window.XMLHttpRequest
+        xmlHttp = new XMLHttpRequest();
+    else
+        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
+    #xmlHttp.withCredentials = true
+    # GET URL
+    xmlHttp.open("GET", url, true);
+    xmlHttp.send(null);
+    
+    xmlHttp.onreadystatechange = (state) ->
+            if xmlHttp.readyState == 4 and xmlHttp.status == 500
+                if snap
+                    snap(xstate)
+            if xmlHttp.readyState == 4 and xmlHttp.status == 200
+                if callback
+                    # Try to parse as JSON and deal with cache objects, fall back to old style parse-and-pass
+                    try
+                        response = JSON.parse(xmlHttp.responseText)
+                        callback(response, xstate);
+                    catch e
+                        callback(JSON.parse(xmlHttp.responseText), xstate)
+
+post = (url, args, xstate, callback, snap) ->
+    xmlHttp = null;
+    # Set up request object
+    if window.XMLHttpRequest
+        xmlHttp = new XMLHttpRequest();
+    else
+        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
+    xmlHttp.withCredentials = true
+    # Construct form data
+    ar = []
+    for k,v of args
+        if v and v != ""
+            ar.push(k + "=" + escape(v))
+    fdata = ar.join("&")
+    
+    
+    # POST URL
+    xmlHttp.open("POST", url, true);
+    xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+    xmlHttp.send(fdata);
+    
+    xmlHttp.onreadystatechange = (state) ->
+            if xmlHttp.readyState == 4 and xmlHttp.status == 500
+                if snap
+                    snap(xstate)
+            if xmlHttp.readyState == 4 and xmlHttp.status == 200
+                if callback
+                    # Try to parse as JSON and deal with cache objects, fall back to old style parse-and-pass
+                    try
+                        response = JSON.parse(xmlHttp.responseText)
+                        callback(response, xstate);
+                    catch e
+                        callback(JSON.parse(xmlHttp.responseText), xstate)
+
+
+postJSON = (url, json, xstate, callback, snap) ->
+    xmlHttp = null;
+    # Set up request object
+    if window.XMLHttpRequest
+        xmlHttp = new XMLHttpRequest();
+    else
+        xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
+    xmlHttp.withCredentials = true
+    # Construct form data
+    fdata = JSON.stringify(json)
+    
+    # POST URL
+    xmlHttp.open("POST", url, true);
+    xmlHttp.setRequestHeader("Content-type", "application/json");
+    xmlHttp.send(fdata);
+    
+    xmlHttp.onreadystatechange = (state) ->
+            if xmlHttp.readyState == 4 and xmlHttp.status == 500
+                if snap
+                    snap(xstate)
+            if xmlHttp.readyState == 4 and xmlHttp.status == 200
+                if callback
+                    # Try to parse as JSON and deal with cache objects, fall back to old style parse-and-pass
+                    try
+                        response = JSON.parse(xmlHttp.responseText)
+                        callback(response, xstate);
+                    catch e
+                        callback(JSON.parse(xmlHttp.responseText), xstate)
+
+
+mk = (t, s, tt) ->
+    r = document.createElement(t)
+    if s
+        for k, v of s
+            if v
+                r.setAttribute(k, v)
+    if tt
+        if typeof tt == "string"
+            app(r, txt(tt))
+        else
+            if isArray tt
+                for k in tt
+                    if typeof k == "string"
+                        app(r, txt(k))
+                    else
+                        app(r, k)
+            else
+                app(r, tt)
+    return r
+
+app = (a,b) ->
+    if isArray b
+        for item in b
+            if typeof item == "string"
+                item = txt(item)
+            a.appendChild(item)
+    else
+        if typeof b == "string"
+            item = txt(b)
+            a.appendChild(item)
+        else
+            a.appendChild(b)
+
+
+set = (a, b, c) ->
+    return a.setAttribute(b,c)
+
+txt = (a) ->
+    return document.createTextNode(a)
+
+get = (a) ->
+    return document.getElementById(a)
+
+swi = (obj) ->
+    switchery = new Switchery(obj, {
+                color: '#26B99A'
+            })
+
+cog = (div, size = 200) ->
+        idiv = document.createElement('div')
+        idiv.setAttribute("class", "icon")
+        idiv.setAttribute("style", "text-align: center; vertical-align: middle; height: 500px;")
+        i = document.createElement('i')
+        i.setAttribute("class", "fa fa-spin fa-cog")
+        i.setAttribute("style", "font-size: " + size + "pt !important; color: #AAB;")
+        idiv.appendChild(i)
+        idiv.appendChild(document.createElement('br'))
+        idiv.appendChild(document.createTextNode('Loading, hang on tight..!'))
+        div.innerHTML = ""
+        div.appendChild(idiv)
+
+isArray = ( value ) ->
+    value and
+        typeof value is 'object' and
+        value instanceof Array and
+        typeof value.length is 'number' and
+        typeof value.splice is 'function' and
+        not ( value.propertyIsEnumerable 'length' )
\ No newline at end of file

Modified: comdev/helpwanted.apache.org/site/js/hw.js
URL: http://svn.apache.org/viewvc/comdev/helpwanted.apache.org/site/js/hw.js?rev=1755684&r1=1755683&r2=1755684&view=diff
==============================================================================
--- comdev/helpwanted.apache.org/site/js/hw.js (original)
+++ comdev/helpwanted.apache.org/site/js/hw.js Wed Aug 10 07:50:59 2016
@@ -1,3 +1,107 @@
+// Generated by CoffeeScript 1.9.3
+var app, brights, cjson, cog, diff, diff_explanation, displayItem, displayItems, doForm, fetch, fetchItems, fetchItemsAdmin, genColors, get, hsl2rgb, hw_weburl, isArray, langs, max_items, mk, pastels, populateAdminForm, populateForm, post, postJSON, projects, reallyPopulate, renderItem, set, showSplash, splashOptions, spoken_langs, sw, swi, txt, types, website_langs, wizard, wstate;
+
+hsl2rgb = function(h, s, l) {
+  var fract, min, sh, sv, switcher, v, vsf;
+  h = h % 1;
+  if (s > 1) {
+    s = 1;
+  }
+  if (l > 1) {
+    l = 1;
+  }
+  if (l <= 0.5) {
+    v = l * (1 + s);
+  } else {
+    v = l + s - l * s;
+  }
+  if (v === 0) {
+    return {
+      r: 0,
+      g: 0,
+      b: 0
+    };
+  }
+  min = 2 * l - v;
+  sv = (v - min) / v;
+  sh = (6 * h) % 6;
+  switcher = Math.floor(sh);
+  fract = sh - switcher;
+  vsf = v * sv * fract;
+  switch (switcher) {
+    case 0:
+      return {
+        r: v,
+        g: min + vsf,
+        b: min
+      };
+    case 1:
+      return {
+        r: v - vsf,
+        g: v,
+        b: min
+      };
+    case 2:
+      return {
+        r: min,
+        g: v,
+        b: min + vsf
+      };
+    case 3:
+      return {
+        r: min,
+        g: v - vsf,
+        b: v
+      };
+    case 4:
+      return {
+        r: min + vsf,
+        g: min,
+        b: v
+      };
+    case 5:
+      return {
+        r: v,
+        g: min,
+        b: v - vsf
+      };
+  }
+  return {
+    r: 0,
+    g: 0,
+    b: 0
+  };
+};
+
+genColors = function(numColors, saturation, lightness, hex) {
+  var baseHue, c, cls, h, i, j, ref;
+  cls = [];
+  baseHue = 1.34;
+  for (i = j = 1, ref = numColors; 1 <= ref ? j <= ref : j >= ref; i = 1 <= ref ? ++j : --j) {
+    c = hsl2rgb(baseHue, saturation, lightness);
+    if (hex) {
+      h = (Math.round(c.r * 255 * 255 * 255) + Math.round(c.g * 255 * 255) + Math.round(c.b * 255)).toString(16);
+      while (h.length < 6) {
+        h = '0' + h;
+      }
+      h = '#' + h;
+      cls.push(h);
+    } else {
+      cls.push({
+        r: parseInt(c.r * 255),
+        g: parseInt(c.g * 255),
+        b: parseInt(c.b * 255)
+      });
+    }
+    baseHue -= 0.23;
+    if (baseHue < 0) {
+      baseHue += 1;
+    }
+  }
+  return cls;
+};
+
+
 /*
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
@@ -13,585 +117,738 @@
  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.
-*/
+ */
 
-var hw_oldstate
+hw_weburl = new RegExp("(" + "(?:(?:https?|ftp)://)" + "(?:\\S+(?::\\S*)?@)?" + "(?:" + "(?!(?:10|127)(?:\\.\\d{1,3}){3})" + "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" + "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" + "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" + "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" + "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" + "|" + "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" + "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" + "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" + "\\.?" + ")" + "(?::\\d{2,5})?" + "(?:[/?#]([^,<>()\\[\\] \\t\\r\\n]|(<[^:\\s]*?>|\\([^:\\s]*?\\)|\\[[^:\\s]*?\\]))*)?" + ")\\.?", "mig");
 
-var hw_weburl = new RegExp(
-  "(" +
-    // protocol identifier
-    "(?:(?:https?|ftp)://)" +
-    // user:pass authentication
-    "(?:\\S+(?::\\S*)?@)?" +
-    "(?:" +
-      // IP address exclusion
-      // private & local networks
-      "(?!(?:10|127)(?:\\.\\d{1,3}){3})" +
-      "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +
-      "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" +
-      // IP address dotted notation octets
-      // excludes loopback network 0.0.0.0
-      // excludes reserved space >= 224.0.0.0
-      // excludes network & broacast addresses
-      // (first & last IP address of each class)
-      "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
-      "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
-      "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
-    "|" +
-      // host name
-      "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
-      // domain name
-      "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" +
-      // TLD identifier
-      "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" +
-      // TLD may end with dot
-      "\\.?" +
-    ")" +
-    // port number
-    "(?::\\d{2,5})?" +
-    // resource path
-    "(?:[/?#]([^,<>()\\[\\] \\t\\r\\n]|(<[^:\\s]*?>|\\([^:\\s]*?\\)|\\[[^:\\s]*?\\]))*)?" +
-    ")\\.?"
-  , "mig"
-);
-
-
-function hw_hsl2rgb (h,s,l)
-{
-    var min, sv, switcher, fract, vsf;
-    h = h % 1;
-    if (s > 1) s = 1;
-    if (l > 1) l = 1;
-    var v = (l <= 0.5) ? (l * (1 + s)) : (l + s - l * s);
-    if (v === 0)
-        return { r: 0, g: 0, b: 0 };
-
-    min = 2 * l - v;
-    sv = (v - min) / v;
-    var sh = (6 * h) % 6;
-    switcher = Math.floor(sh);
-    fract = sh - switcher;
-    vsf = v * sv * fract;
-
-    switch (switcher)
-    {
-        case 0: return { r: v, g: min + vsf, b: min };
-        case 1: return { r: v - vsf, g: v, b: min };
-        case 2: return { r: min, g: v, b: min + vsf };
-        case 3: return { r: min, g: v - vsf, b: v };
-        case 4: return { r: min + vsf, g: min, b: v };
-        case 5: return { r: v, g: min, b: v - vsf };
-    }
-    return {r:0, g:0, b: 0};
-}
-
-function genColors(numColors, saturation, lightness) {
-    var cls = []
-    var baseHue = 1.32;
-    for (var i = 0; i < numColors; i++) {
-        
-        var c = hw_hsl2rgb(baseHue, saturation, lightness)
-        cls.push({r:parseInt(c.r*255), g:parseInt(c.g*255), b:parseInt(c.b*255)})
-        baseHue -= 0.175
-        if (baseHue < 0) {
-            baseHue += 1
-        }
-    }
-    return cls
-}
+pastels = genColors(16, 0.9, 0.85);
 
-var pastels = genColors(16, 0.9, 0.85)
-var brights = genColors(16, 0.8, 0.675)
+brights = genColors(16, 0.8, 0.675);
 
-var splashOptions = {
-    'I want to help out!': 'this.parentNode.style.display="none"; wizard(1);',
-    'I am an Apache committer and wish to add or edit tasks.': 'location.href="/admin/";',
-    'I want to help spread the word about <q>Help Wanted</q>!': 'location.href="wtest.html";',
-}
-
-
-var diff = ['Beginner', 'Journeyman', 'Intermediate', 'Advanced', 'Expert']
-var langs = ['c', 'xml', 'c++', 'c-sharp', 'java', 'javascript', 'css', 'html', 'perl', 'ruby', 'lua', 'python', 'go', 'rust', 'erlang', 'swift', 'groovy', 'haskell', 'scala', 'php', 'bash', 'tcl', 'jsp', 'svg']
-
-var types = ['programming', 'web design', 'marketing', 'documentation', 'community', 'translation']
-var types_long = {
-    programming: "Programming and Development",
-    'web design': "Web Design",
-    marketing: 'Marketing and Publicity',
-    documentation: 'Documentation and Guides',
-    community: 'Community Outreach',
-    translation: 'Translation'
-}
-
-var diff_explanation = [
-    'This is an easy task that anyone can get started on',
-    'This requires a bit of knowledge of the project, but otherwise is an easy task',
-    'This requires a good knowledge of the project',
-    'This requires a good knowledge of the project and good technical skills',
-    'This requires intimate knowledge of the project and excellent technical skills'
-]
-
-var spoken_langs = ['english', 'french', 'german', 'spanish', 'russian', 'italian', 'japanese', 'chinese']
-var website_langs = ['css','javascript','html']
-var projects = ['all projects']
-var cjson = {}
-var max_items = 10
-
-langs.sort()
-types.sort()
-
-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.onreadystatechange = function(state) {
-
-		if (xmlHttp.readyState == 4 && xmlHttp.status == 200 || xmlHttp.status == 404) {
-			if (callback) {
-				if (xmlHttp.status == 404) {
-					callback({}, xstate);
-				} else {
-					callback(JSON.parse(xmlHttp.responseText), xstate);
-				}
-			}
-		}
-	}
-}
-
-function showSplash(args) {
-    var i = 0;
-    var obj = document.getElementById('splash')
-    document.getElementById('pickerparent').style.display = "none"
-    document.getElementById('wizard_step1').style.display = "none"
-    obj.style.display = "block"
-    obj.innerHTML = "<span style='font-size: 20pt; font-family: sans-serif; color: #FFF;'>What would you like to do today?</span>"
-    for (var title in splashOptions) {
-        var p = pastels[i]
-        var b = brights[i]
-        var d = document.createElement('div')
-        d.setAttribute("class", "option")
-        d.setAttribute("onclick", splashOptions[title])
-        d.style.background = "rgba(" + p.r + "," + p.g + "," + p.b + ", 0.85)"
-        var btn = document.createElement('div')
-        btn.style.background = "rgba(" + b.r + "," + b.g + "," + b.b + ", 1)"
-        d.appendChild(btn)
-        d.innerHTML += title; //appendChild(document.createTextNode(title))
-        obj.appendChild(d)
-        i++
-    }
-}
-
-var wstate = {}
-
-function wizard(step, arg) {
-    var obj = document.getElementById('innerpicker')
-    var pobj = document.getElementById('pickerparent')
-    var sobj = document.getElementById('splash')
-    var wobj = document.getElementById('wizard_step1')
-    if (step == 0) {
-        pobj.style.display = "none"
-        wobj.style.display = "none"
-        sobj.style.display = "block"
-        
-    } else if (step > 1) {
-        pobj.style.display = "block"
-    }
-    document.getElementById('hwitems').innerHTML = ""
-    if (!step) {
-        step = 1
-    }
-    if (step == 1) {
-        wstate = {}
-        wobj.style.display = "block"
-        var wcobj = document.getElementById('wizard_step1_contents')
-        wcobj.innerHTML = "<span style='font-size: 20pt; font-family: sans-serif; color: #FFF;'>What kind of tasks would you like to help with?</span>"
-        for (var i in types) {
-            var p = pastels[i]
-            var b = brights[i]
-            var d = document.createElement('div')
-            d.setAttribute("class", "option")
-            d.setAttribute("onclick", "wizard(2, '" + types[i] + "');")
-            d.style.background = "rgba(" + p.r + "," + p.g + "," + p.b + ", 0.85)"
-            var btn = document.createElement('img')
-            btn.setAttribute("src", "images/" + types[i].replace(/\s+/g, "") + "_large.png")
-            btn.setAttribute("style", "width: 36px; heigh: 36px; vertical-align: middle; padding: 4px; padding-right: 12px;")
-            d.appendChild(btn)
-            d.appendChild(document.createTextNode(types_long[types[i]]))
-            wcobj.appendChild(d)
-        }
-        
-        // show me everything
-        var i = types.length
-        var p = pastels[i]
-            var b = brights[i]
-            var d = document.createElement('div')
-            d.setAttribute("class", "option")
-            d.setAttribute("onclick", "fetchItems();")
-            d.style.background = "rgba(" + p.r + "," + p.g + "," + p.b + ", 0.85)"
-            var btn = document.createElement('img')
-            btn.setAttribute("src", "images/everything_large.png")
-            btn.setAttribute("style", "width: 36px; heigh: 36px; vertical-align: middle; padding: 4px; padding-right: 12px;")
-            d.appendChild(btn)
-            d.appendChild(document.createTextNode("Just show me everything..."))
-            wcobj.appendChild(d)
-        
-    }
-    if (step == 2 && arg) {
-        wstate = {
-            type: arg
-        }
-        var tl = types_long[arg]
-        var wcobj = document.getElementById('wizard_step1_contents')
-        wcobj.innerHTML = "<span style='font-size: 20pt; font-family: sans-serif; color: #FFF;'>" + tl + "</span>"
-        var obj = document.createElement('div')
-        obj.setAttribute("class", "optiontwo")
-        wcobj.appendChild(obj)
-        var i = 0;
-        for (var k in types) {
-            if (types[k] == arg) {
-                i = k;
-                break
-            }
-        }
-        var p = pastels[i]
-        var b = brights[i]
-        obj.style.background = "rgba(" + p.r + "," + p.g + "," + p.b + ", 0.95)"
-        obj.style.color = "#333 !important"
-        obj.style.height = "340px"
-        
-        obj.innerHTML += "<h2 style='margin-bottom: 4px; line-height: 18pt; text-align: center;'>Which languages are you proficient in?</h2>"
-        obj.innerHTML += "<p>You don't need to pick a language, but it will help narrow down the tasks available for you.</p>"
-        
-        if (arg == 'programming' || arg == 'documentation') {
-            for (var i in langs) {
-                var div = document.createElement('div')
-                div.style.float = "left"
-                div.style.width = "100px"
-                div.style.height = "28px"
-                var cb = document.createElement('input')
-                cb.setAttribute("type", "checkbox")
-                cb.setAttribute("id", "plang_" + langs[i])
-                var lbl = document.createElement('label')
-                lbl.setAttribute("for", "plang_" + langs[i])
-                var txt = document.createTextNode(langs[i])
-                lbl.appendChild(txt)
-                div.appendChild(cb)
-                div.appendChild(lbl)
-                obj.appendChild(div)
-            }
-            obj.appendChild(document.createElement('br'))
-        } else if (arg == 'web design') {
-            for (var i in website_langs) {
-                var div = document.createElement('div')
-                div.style.float = "left"
-                div.style.width = "100px"
-                div.style.height = "28px"
-                var cb = document.createElement('input')
-                cb.setAttribute("type", "checkbox")
-                cb.setAttribute("id", "plang_" + website_langs[i])
-                var lbl = document.createElement('label')
-                lbl.setAttribute("for", "plang_" + website_langs[i])
-                var txt = document.createTextNode(website_langs[i])
-                lbl.appendChild(txt)
-                div.appendChild(cb)
-                div.appendChild(lbl)
-                obj.appendChild(div)
-            }
-            obj.appendChild(document.createElement('br'))
-        } else {
-            for (var i in spoken_langs) {
-                var div = document.createElement('div')
-                div.style.float = "left"
-                div.style.width = "100px"
-                div.style.height = "28px"
-                var cb = document.createElement('input')
-                cb.setAttribute("type", "checkbox")
-                cb.setAttribute("id", "plang_" + spoken_langs[i])
-                var lbl = document.createElement('label')
-                lbl.setAttribute("for", "plang_" + spoken_langs[i])
-                var txt = document.createTextNode(spoken_langs[i].replace(/^([a-z])/, function(a) { return a.toUpperCase() }))
-                lbl.appendChild(txt)
-                div.appendChild(cb)
-                div.appendChild(lbl)
-                obj.appendChild(div)
-            }
-            obj.appendChild(document.createElement('br'))
-        }
-        obj.innerHTML += '<div style="height: 60px; width: 100%; margin-top: 30px; float: left;"><input type="button" class="finishbutton" onclick="doForm(this.parentNode.parentNode)" value="Find me something to do!"/>' +
-        '<span style="font-size: 14pt; color: #333;">◀ <a style="color: #333;" onclick="wizard(1)" href="javascript:void(0);">Back to start</a></span></div>'
-    }
-}
-
-function populateForm() {
-    // languages
-    
-    var f = document.getElementById('plang')
-    for (var i in langs) {
-        var div = document.createElement('div')
-        div.style.float = "left"
-        div.style.width = "100px"
-        var cb = document.createElement('input')
-        cb.setAttribute("type", "checkbox")
-        cb.setAttribute("id", "plang_" + langs[i])
-        var lbl = document.createElement('label')
-        lbl.setAttribute("for", "plang_" + langs[i])
-        var txt = document.createTextNode(langs[i])
-        lbl.appendChild(txt)
-        div.appendChild(cb)
-        div.appendChild(lbl)
-        f.appendChild(div)
-    }
-    f.appendChild(document.createElement('br'))
-    
-    // task types
-
-    var f = document.getElementById('ptypes')
-    for (var i in types) {
-        var div = document.createElement('div')
-        div.style.float = "left"
-        div.style.width = "200px"
-        var cb = document.createElement('input')
-        cb.setAttribute("type", "checkbox")
-        cb.setAttribute("id", "ptype_" + types[i])
-        var lbl = document.createElement('label')
-        lbl.setAttribute("for", "ptype_" + types[i])
-        var txt = document.createTextNode(types[i])
-        lbl.appendChild(txt)
-        div.appendChild(cb)
-        div.appendChild(lbl)
-        f.appendChild(div)
-    }
-    f.appendChild(document.createElement('br'))
-    
-    // projects - none for now
-    var f = document.getElementById('pprojects')
-    f.appendChild(document.createTextNode('All projects'))
-}
-
-function doForm(par) {
-    var l = []
-    var t = []
-    var p = []
-    for (var i in langs) {
-        if (document.getElementById("plang_" + langs[i]) && document.getElementById("plang_" + langs[i]).checked) {
-            l.push(langs[i])
-        }
-    }
-    for (var i in types) {
-        if (document.getElementById("ptype_" + types[i]) && document.getElementById("ptype_" + types[i]).checked) {
-            t.push(types[i])
-        }
-    }
-    if (wstate.type) {
-        t = [wstate.type]
-    }
-    if (wstate.projects) {
-        p = wstate.projects
-    }
-    fetchItems(l, t, p, null, par)
-}
-
-function sw(id) {
-    var obj = document.getElementById(id)
-    var op = obj.style.display == 'none' ? 'table-row' : 'none'
-    obj.style.display = op
-}
-
-function reallyPopulate(json, state) {
-    var pro = []
-    var obj = document.getElementById('project')
-    
-    // optgroup for spoken/written
-    var optg = document.createElement('optgroup')
-    optg.label = state ? "Non-TLPs:" : "Top Level Projects:"
-    obj.appendChild(optg)
-    
-    for (var i in (json.committees || json.groups)) {
-        pro.push(i)
-        var opt = document.createElement('option')
-        opt.text = i
-        opt.setAttribute("value", i)
-        obj.appendChild(opt)
-    }
-    if (state) {
-        return
-    }
-    var obj = document.getElementById('languages')
-    // optgroup for programming
-    var optg = document.createElement('optgroup')
-    optg.label = "Programming languages"
-    obj.appendChild(optg)
-    
-    // prog languages
-    for (var i in langs) {
-        var opt = document.createElement('option')
-        opt.text = langs[i]
-        opt.setAttribute("value", langs[i])
-        obj.appendChild(opt)
-    }
-    
-    // optgroup for spoken/written
-    var optg = document.createElement('optgroup')
-    optg.label = "Spoken languages"
-    obj.appendChild(optg)
-    
-    for (var i in spoken_langs) {
-        var opt = document.createElement('option')
-        opt.text = spoken_langs[i].replace(/^([a-z])/, function(a) {return a.toUpperCase()})
-        opt.setAttribute("value", spoken_langs[i])
-        obj.appendChild(opt)
-    }
-    if (!state) {
-        getAsyncJSON('https://whimsy.apache.org/public/public_nonldap_groups.json', 'other', reallyPopulate)
-    }
-}
-
-function populateAdminForm() {
-    getAsyncJSON('https://whimsy.apache.org/public/public_ldap_committees.json', false, reallyPopulate)   
-}
-
-function displayItems(json, state) {
-    json = json ? json.tasks : cjson
-    cjson = json
-    var numItems = 0
-    for (var i in json) {
-        var item = json[i]
-        if (item.closed) {
-            continue
-        }
-        numItems++
-    }
-    if (numItems == 0 && state && state.parentObject) {
-        state.parentObject.style.display = 'none'
-        window.setTimeout(function() { state.parentObject.style.display = 'block' }, 50)
-        state.parentObject.innerHTML ="<div style='line-height: 12pt; text-align: center;'><h2>Dang it!</h2><p>Sorry, we couldn't find any open tasks matching your criteria.<br/>You could try expanding your search or picking a different category.<br/><br/></p><p><span style='font-size: 14pt; color: #333;'>◀ <a style='color: #333;' onclick='wizard(1)' href='javascript:void(0);'>Back to start</a></span></p></div>"
-        return
-    }
-    if (state && typeof(state) === "string") {
-        if (hw_oldstate == state) {
-            json.sort(function(a,b) { return a[state] < b[state] })
-            hw_oldstate = ""
-        } else {
-            json.sort(function(a,b) { return a[state] > b[state] })
-            hw_oldstate = state
-        }
-        
-    }
-    
-    var obj = document.getElementById('hwitems')
-    
-    if (typeof(numItems) === 'undefined') {
-        obj.innerHTML = "Sorry, we couldn't find any tasks matching your criteria"
-        return
-    }
-    
-    obj.innerHTML = "<p id='hwrtable'>Found " + numItems + " item" + (numItems != 1 ? "s" : "") + " you might be interested in:</p>"
-    var colors = genColors(numItems+2)
-    
-    var admintab = ""
-    if (state.admin) {
-        admintab = "<th>Actions</th>"
-    }
-    var tbl = "<table style='text-align: left; width: 100%;'>" +
-    "<tr style='cursor: pointer' title='Click on a column to sort'><th>&nbsp;</th><th onclick='displayItems(null, \"project\");'>Project</th>" +
-    "<th onclick='displayItems(null, \"title\");'>Title</th>" +
-    "<th onclick='displayItems(null, \"languages\");'>Languages</th>" +
-    "<th onclick='displayItems(null, \"difficulty\");'>Difficulty</th>" +
-    "<th onclick='displayItems(null, \"created\");'>Created</th>" +
-    admintab + "</tr>"
-    for (var i in json) {
-        var item = json[i]
-        if (item.closed) {
-            continue
-        }
-        if (parseInt(i) >= max_items) {
-            tbl += "<tr><td colspan='5'><a href='javascript:void(0);' onclick='max_items += 10; displayItems(null, {});'>Show more tasks</a></td></tr>"
-            break
-        }
-        var z = parseInt(i)+1
-        var ptype = item.type.replace(/\s+/g, "")
-        var cdate = new Date(item.created*1000).toLocaleString()
-        var lingos = (item.languages != 'n/a') ? item.languages.split(",").join(", ") : ""
-        
-        // admin stuff
-        var add = ""
-        if (state.admin) {
-            add = " <td><a href='/admin/close.lua?id=" + item.request_id + "'>Mark as done</a></td>"
-        }
-        item.description = item.description.replace(/\n/g, "<br/>").replace(hw_weburl, function(a) { return "<a href='"+a+"'>"+a+"</a>"})
-        tbl += "<tr style='cursor: pointer;' onclick=\"sw('details_" + i + "');\"><td width='68'><div class='itemNumber-yellow'>" + z + "</div><img title='" + item.type + "' style='float: left; width: 24px; height: 24px;' src='https://helpwanted.apache.org/images/icon_" + ptype + ".png'/></td>" +
-        "<td>" + item.project + "</td>"+
-        "<td style='text-align: left;'>" + item.title + "</td>" +
-        "<td>" + lingos + "</td><td title='" + diff_explanation[parseInt(item.difficulty)] + "' style='text-align: left;'><img style='width: 20px; height: 20px; vertical-align: middle;' src='https://helpwanted.apache.org/images/level_" + (parseInt(item.difficulty)+1) + ".png'/> " + diff[item.difficulty] + "</td><td>" + cdate + "</td>" + add + "</tr>"
-        
-        tbl += "<tr style='display:none;' id='details_" + i + "'><td colspan='6'><b>Project:</b> " + item.project + "<br/><b>Requested by:</b> " + item.author + "@apache.org<br/><b>Created:</b> " + cdate + "<br/><b>Description:</b> <blockquote>" + item.description + "</blockquote><b>Further information: </b> <a href='" + item.url + "'>" + item.url + "</a><br/><input type='button' onclick='location.href=\"https://helpwanted.apache.org/task.html?" + item.request_id +"\";' value='I am interested in this'/></td></tr>"
-        
-    }
-    tbl += "</table>"
-    obj.innerHTML += tbl
-    if (location.href.search('admin') == -1) {
-        location.hash = '#hwrtable'
-    }
-    
-}
-
-function fetchItems(languages, types, projects, sortBy, par) {
-    if (!languages) languages = []
-    if (!types) types = []
-    if (!projects) projects = []
-    getAsyncJSON("https://helpwanted.apache.org/tasks.lua?lang=" + languages.join(",") + "&type=" + types.join(",") +"&project=" + projects.join(","),
-                 {
-                    languages: languages,
-                    types: types,
-                    projects: projects,
-                    sortBy: sortBy,
-                    parentObject: par
-                    }, displayItems)
-}
-
-
-function fetchItemsAdmin() {
-    getAsyncJSON("https://helpwanted.apache.org/tasks.lua", {admin: true}, displayItems)
-}
-
-function renderItem(json, state) {
-    var p = pastels[parseInt(Math.random()*pastels.length)]
-    var obj = document.getElementById('item')
-    var cdate = new Date(json.created*1000).toDateString()
-    var rid = json.request_id.substring(0,8)
-    var overrides = {
-        comdev: 'community',
-        whimsy: 'whimsical'
-    }
-    if (overrides[json.project]) {
-        json.project = overrides[json.project]
-    }
-    json.description = json.description.replace(/\n/g, "<br/>").replace(hw_weburl, function(a) { return "<a href='"+a+"'>"+a+"</a>"})
-    obj.innerHTML = "<h2>Task #" + state.substring(0,8) + ": " + json.title + "</h2>"
-    var mlink = "mailto:dev@" + json.project + ".apache.org?subject=" + escape("Help with task: " + json.title) + "&body=" + escape("I would like to help out with the task listed at https://helpwanted.apache.org/task.html?" + rid + "\n\n")
-    var rgba = "rgba(" + p.r + "," + p.g + "," + p.b + ", 1)"
-    obj.innerHTML += "<div id='pickerparent' style='background: " + rgba + "; padding:12px;'><p style='text-align: left;'><b>Project: </b> " + json.project + "<br/>" +
-        "<b>Created by:</b> " + json.author + "@apache.org<br/>" +
-        "<b>Task added: </b>" + cdate + "<br/>" +
-        "<b>Difficulty: </b> <img style='width: 16px; height: 16px; vertical-align: middle;' src='/images/level_" + (parseInt(json.difficulty)+1) + ".png'/> " + diff[json.difficulty] + " - " + diff_explanation[parseInt(json.difficulty)] + "<br/>" +
-        "<b>Task type:</b> " + types_long[json.type] + "<br/>" +
-        (json.url && json.url.length > 10 ? "<b>Additional information:</b> <a href='" + json.url + "'>" + json.url + "</a><br/>" : "")+
-        ((json.estimate && json.estimate.length > 0) ? "<b>Estimated time to complete:</b> " + json.estimate : "") +
-        ((json.timeout && json.timeout > 0) ? "<b>Task expires:</b> " + new Date(json.timeout*1000).toDateString() : "") +
-        "<blockquote style='text-align: left;'><q>" + json.description + "</q></blockquote>" +
-        "<br/></p>" +
-        "<h3 style='text-align: left;'>How to help:</h3><p style='text-align: left;'>" +
-        (json.curl && json.curl.length > 10 ? "<b>Contributor's guide for this project: </b><a href='" + json.curl + "'>" + json.curl + "</a><br/>" :"") +
-        "If you want to help with this task, please get in touch with the project at: <a href=\""+mlink+"\">dev@" + json.project + ".apache.org</a>!" +
-        "<br/>You should also check out the additional information URL (if such is provided above) for more information."
-        "<br/>&nbsp;<br/>&nbsp;<br/></p></div>"
-}
-
-function displayItem(id) {
-    getAsyncJSON("https://helpwanted.apache.org/tasks.lua?id=" + id, id, renderItem)
-}
\ No newline at end of file
+splashOptions = {
+  'I want to help out!': 'this.parentNode.style.display="none"; wizard(1);',
+  'I am an Apache committer and wish to add or edit tasks.': 'location.href="/admin/";',
+  'I want to help spread the word about "Help Wanted"!': 'location.href="wtest.html";'
+};
+
+diff = ['Beginner', 'Journeyman', 'Intermediate', 'Advanced', 'Expert'];
+
+diff_explanation = ['This is an easy task that anyone can get started on', 'This requires a bit of knowledge of the project, but otherwise is an easy task', 'This requires a good knowledge of the project', 'This requires a good knowledge of the project and good technical skills', 'This requires intimate knowledge of the project and excellent technical skills'];
+
+langs = ['c', 'xml', 'c++', 'c-sharp', 'java', 'javascript', 'css', 'html', 'perl', 'ruby', 'lua', 'python', 'go', 'rust', 'erlang', 'swift', 'groovy', 'haskell', 'scala', 'php', 'bash', 'tcl', 'jsp', 'svg'];
+
+spoken_langs = ['english', 'french', 'german', 'spanish', 'russian', 'italian', 'japanese', 'chinese'];
+
+website_langs = ['css', 'javascript', 'html'];
+
+types = {
+  programming: "Programming and Development",
+  'web design': "Web Design",
+  marketing: 'Marketing and Publicity',
+  documentation: 'Documentation and Guides',
+  community: 'Community Outreach',
+  translation: 'Translation'
+};
+
+projects = ['all projects'];
+
+cjson = {};
+
+max_items = 10;
+
+langs.sort();
+
+showSplash = function(args) {
+  var b, btn, d, i, obj, oc, p, results, title;
+  obj = get('splash');
+  get('pickerparent').style.display = "none";
+  get('wizard_step1').style.display = "none";
+  obj.style.display = "block";
+  obj.innerHTML = "";
+  app(obj, mk('span', {
+    style: "font-size: 20pt; font-family: sans-serif; color: #FFF;"
+  }, "What would you like to do today?"));
+  i = 0;
+  results = [];
+  for (title in splashOptions) {
+    oc = splashOptions[title];
+    p = pastels[i];
+    b = brights[i];
+    d = mk('div', {
+      "class": "option",
+      onclick: oc,
+      style: "background: rgba(" + p.r + "," + p.g + "," + p.b + ", 0.85);"
+    });
+    btn = mk('div', {
+      style: "background: rgba(" + b.r + "," + b.g + "," + b.b + ", 1);"
+    });
+    app(d, btn);
+    app(d, title);
+    app(obj, d);
+    results.push(i++);
+  }
+  return results;
+};
+
+wstate = {};
+
+wizard = function(step, arg) {
+  var b, btn, cb, d, desc, div, fbtn, fdiv, fspan, i, j, lang, lbl, len, len1, len2, m, n, obj, p, pobj, sobj, type, wcobj, wobj;
+  obj = get('innerpicker');
+  pobj = get('pickerparent');
+  sobj = get('splash');
+  wobj = get('wizard_step1');
+  if (step === 0) {
+    pobj.style.display = "none";
+    wobj.style.display = "none";
+    sobj.style.display = "block";
+  } else if (step > 1) {
+    pobj.style.display = "block";
+  }
+  get('hwitems').innerHTML = "";
+  if (!step || step === 0) {
+    step = 1;
+  }
+  if (step === 1) {
+    wstate = {};
+    wobj.style.display = "block";
+    wcobj = get('wizard_step1_contents');
+    wcobj.innerHTML = "";
+    app(wcobj, mk('span', {
+      style: 'font-size: 20pt; font-family: sans-serif; color: #FFF;'
+    }, "What kind of tasks would you like to help with?"));
+    i = 0;
+    for (type in types) {
+      desc = types[type];
+      p = pastels[i];
+      b = brights[i];
+      d = mk('div', {
+        "class": "option",
+        onclick: "wizard(2, '" + type + "');",
+        style: "background: rgba(" + p.r + "," + p.g + "," + p.b + ", 0.85);"
+      });
+      btn = mk('img', {
+        src: "images/" + type.replace(/\s+/g, "") + "_large.png",
+        style: "width: 36px; heigh: 36px; vertical-align: middle; padding: 4px; padding-right: 12px;"
+      });
+      app(d, btn);
+      app(d, desc);
+      app(wcobj, d);
+      i++;
+    }
+    p = pastels[i];
+    b = brights[i];
+    d = mk('div', {
+      "class": "option",
+      onclick: "fetchItems();",
+      style: "background:rgba(" + p.r + "," + p.g + "," + p.b + ", 0.85);"
+    });
+    btn = mk('img', {
+      src: "images/everything_large.png",
+      style: "width: 36px; heigh: 36px; vertical-align: middle; padding: 4px; padding-right: 12px;"
+    });
+    app(d, btn);
+    app(d, "Just show me everything...");
+    app(wcobj, d);
+  }
+  if (step === 2 && arg) {
+    wstate = {
+      type: arg
+    };
+    desc = types[arg];
+    wcobj = get('wizard_step1_contents');
+    wcobj.innerHTML = "";
+    app(wcobj, mk('span', {
+      style: 'font-size: 20pt; font-family: sans-serif; color: #FFF;'
+    }, desc));
+    obj = mk('div', {
+      "class": "optiontwo"
+    });
+    app(wcobj, obj);
+    i = 0;
+    for (type in types) {
+      desc = types[type];
+      if (type === arg) {
+        break;
+      }
+      i++;
+    }
+    p = pastels[i];
+    b = brights[i];
+    obj.style.background = "rgba(" + p.r + "," + p.g + "," + p.b + ", 0.95)";
+    obj.style.color = "#333 !important";
+    obj.style.height = "340px";
+    app(obj, mk('h2', {
+      style: "margin-bottom: 4px; line-height: 18pt; text-align: center;"
+    }, "Which languages are you proficient in?"));
+    app(obj, mk('p', {}, "You don't need to pick a language, but it will help narrow down the tasks available for you."));
+    if (arg === 'programming' || arg === 'documentation') {
+      for (i = j = 0, len = langs.length; j < len; i = ++j) {
+        lang = langs[i];
+        div = mk('div', {
+          style: "float: left; width: 100px; height: 28px;"
+        });
+        cb = mk('input', {
+          type: "checkbox",
+          id: "plang_" + lang
+        });
+        lbl = mk('label', {
+          "for": "plang_" + lang
+        }, lang);
+        app(div, cb);
+        app(div, lbl);
+        app(obj, div);
+      }
+      app(obj, mk('br'));
+    } else if (arg === 'web design') {
+      for (i = m = 0, len1 = website_langs.length; m < len1; i = ++m) {
+        lang = website_langs[i];
+        div = mk('div', {
+          style: "float: left; width: 100px; height: 28px;"
+        });
+        cb = mk('input', {
+          type: "checkbox",
+          id: "plang_" + lang
+        });
+        lbl = mk('label', {
+          "for": "plang_" + lang
+        }, lang);
+        app(div, cb);
+        app(div, lbl);
+        app(obj, div);
+      }
+      app(obj, mk('br'));
+    } else {
+      for (i = n = 0, len2 = spoken_langs.length; n < len2; i = ++n) {
+        lang = spoken_langs[i];
+        div = mk('div', {
+          style: "float: left; width: 100px; height: 28px;"
+        });
+        cb = mk('input', {
+          type: "checkbox",
+          id: "plang_" + lang
+        });
+        lbl = mk('label', {
+          "for": "plang_" + lang
+        }, lang.replace(/^([a-z])/, (function(_this) {
+          return function(a) {
+            return a.toUpperCase();
+          };
+        })(this)));
+        app(div, cb);
+        app(div, lbl);
+        app(obj, div);
+      }
+      app(obj, mk('br'));
+    }
+    fdiv = mk('div', {
+      style: "height: 60px; width: 100%; margin-top: 30px; float: left;"
+    });
+    fbtn = mk('input', {
+      type: "button",
+      "class": "finishbutton",
+      onclick: "doForm(this.parentNode.parentNode);",
+      value: "Find me something to do!"
+    });
+    fspan = mk('span', {
+      style: "font-size: 14pt; color: #333;"
+    }, "◀");
+    app(fspan, mk('a', {
+      style: "color: #333;",
+      onclick: "wizard(1)",
+      href: "javascript:void(0);"
+    }, "Back to start"));
+    app(fdiv, [fbtn, fspan]);
+    return app(obj, fdiv);
+  }
+};
+
+populateForm = function() {
+  var cb, div, f, j, lang, lbl, len, len1, m, type;
+  f = get('plang');
+  for (j = 0, len = langs.length; j < len; j++) {
+    lang = langs[j];
+    div = mk('div', {
+      style: "float: left; width: 100px;"
+    });
+    cb = mk('input', {
+      type: "checkbox",
+      id: "plang_" + lang
+    });
+    lbl = mk('label', {
+      "for": "plang_" + lang
+    }, lang);
+    app(div, [cb, lbl]);
+    app(f, div);
+  }
+  app(f, mk('br'));
+  f = get('ptypes');
+  for (m = 0, len1 = types.length; m < len1; m++) {
+    type = types[m];
+    div = mk('div', {
+      style: "float: left; width: 200px;"
+    });
+    cb = mk('input', {
+      type: "checkbox",
+      id: "ptype__" + type
+    });
+    lbl = mk('label', {
+      "for": "ptype_" + type
+    }, type);
+    app(div, [cb, lbl]);
+    app(f, div);
+  }
+  app(f, mk('br'));
+  f = get('pprojects');
+  return app(f, "All projects");
+};
+
+doForm = function(par) {
+  var j, l, lang, len, len1, m, p, t, type;
+  l = [];
+  t = [];
+  p = [];
+  for (j = 0, len = langs.length; j < len; j++) {
+    lang = langs[j];
+    if (get("plang_" + lang) && get("plang_" + lang).checked) {
+      l.push(lang);
+    }
+  }
+  for (m = 0, len1 = types.length; m < len1; m++) {
+    type = types[m];
+    if (get("ptype_" + type) && get("ptype" + type).checked) {
+      t.push(type);
+    }
+  }
+  if (wstate.type) {
+    t = [wstate.type];
+  }
+  if (wstate.projects) {
+    p = wstate.projects;
+  }
+  return fetchItems(l, t, p, null, par);
+};
+
+sw = function(id) {
+  var obj;
+  obj = get(id);
+  return obj.style.display = (obj.style.display === 'none' ? 'table-row' : 'none');
+};
+
+reallyPopulate = function(json, state) {
+  var group, j, lang, len, len1, len2, m, n, obj, opt, optg, pro, ref;
+  pro = [];
+  obj = get('project');
+  optg = mk('optgroup', {
+    label: (state ? 'Non-TLPs:' : 'Top Level Projects:')
+  });
+  app(obj, optg);
+  ref = json.committees || json.groups;
+  for (j = 0, len = ref.length; j < len; j++) {
+    group = ref[j];
+    pro.push(group);
+    app(obj, mk('option', {
+      value: group
+    }, group));
+  }
+  if (state) {
+    return;
+  }
+  obj = get('languages');
+  optg = mk('optgroup', {
+    label: "Programming languages:"
+  });
+  app(obj, optg);
+  for (m = 0, len1 = langs.length; m < len1; m++) {
+    lang = langs[m];
+    app(obj, mk('option', {
+      text: lang,
+      value: lang
+    }, lang));
+  }
+  optg = mk('optgroup', {
+    label: "Spoken languages:"
+  });
+  app(obj, optg);
+  for (n = 0, len2 = spoken_langs.length; n < len2; n++) {
+    lang = spoken_langs[n];
+    app(obj, mk('option', {
+      value: lang
+    }, lang.replace(/^([a-z])/, (function(_this) {
+      return function(a) {
+        return a.toUpperCase();
+      };
+    })(this))));
+    opt = document.createElement('option');
+  }
+  if (!state || state === 0) {
+    return fetch('https://whimsy.apache.org/public/public_nonldap_groups.json', 'other', reallyPopulate);
+  }
+};
+
+populateAdminForm = function() {
+  return fetch('https://whimsy.apache.org/public/public_ldap_committees.json', false, reallyPopulate);
+};
+
+displayItems = function(json, state) {
+  var add, admintab, cdate, colors, div, hw_oldstate, i, item, j, len, len1, lingos, m, numItems, obj, ptype, tbl, z;
+  json = json ? json.tasks : cjson;
+  cjson = json;
+  numItems = 0;
+  for (j = 0, len = json.length; j < len; j++) {
+    item = json[j];
+    if (item.closed) {
+      continue;
+    }
+    numItems++;
+  }
+  if (numItems === 0 && state && state.parentObject) {
+    state.parentObject.style.display = 'none';
+    window.setTimeout(function() {
+      return state.parentObject.style.display = 'block';
+    }, 50);
+    state.parentObject.innerHTML = "";
+    div = mk('div', {
+      style: "line-height: 12pt; text-align: center;"
+    }, [
+      mk('h2', {}, "Dang it!"), mk('p', {}, ["Sorry, we couldn't find any open tasks matching your criteria.", mk('br'), "You could try expanding your search or picking a different category.", mk('br'), mk('br')]), mk('p', {}, [
+        mk('span', {
+          style: "font-size: 14pt; color: #333;"
+        }, [
+          "◀", mk('a', {
+            style: "color: #333;",
+            onclick: 'wizard(1)',
+            href: 'javascript:void(0);'
+          }, "Back to start")
+        ])
+      ])
+    ]);
+    app(state.parentObject, div);
+    return;
+  }
+  if (state && typeof state === "string") {
+    if (hw_oldstate === state) {
+      json.sort((function(_this) {
+        return function(a, b) {
+          return a[state] < b[state];
+        };
+      })(this));
+      hw_oldstate = "";
+    } else {
+      json.sort((function(_this) {
+        return function(a, b) {
+          return a[state] > b[state];
+        };
+      })(this));
+      hw_oldstate = state;
+    }
+  }
+  obj = get('hwitems');
+  if (typeof numItems === 'undefined') {
+    obj.innerHTML = "Sorry, we couldn't find any tasks matching your criteria";
+    return;
+  }
+  obj.innerHTML = "<p id='hwrtable'>Found " + numItems + " item" + (numItems !== 1 ? "s" : "") + " you might be interested in:</p>";
+  colors = genColors(numItems + 2);
+  admintab = "";
+  if (state && state.admin) {
+    admintab = "<th style='width: 80px;'>Actions</th>";
+  }
+  tbl = "<table style='text-align: left; width: 100%;'>" + "<tr style='cursor: pointer' title='Click on a column to sort'><th>&nbsp;</th><th onclick='displayItems(null, \"project\");'>Project</th>" + "<th onclick='displayItems(null, \"title\");'>Title</th>" + "<th onclick='displayItems(null, \"languages\");'>Languages</th>" + "<th onclick='displayItems(null, \"difficulty\");'>Difficulty</th>" + "<th onclick='displayItems(null, \"created\");'>Created</th>" + admintab + "</tr>";
+  for (i = m = 0, len1 = json.length; m < len1; i = ++m) {
+    item = json[i];
+    if (item.closed) {
+      continue;
+    }
+    if (i >= max_items) {
+      tbl += "<tr><td colspan='5'><a href='javascript:void(0);' onclick='max_items += 10; displayItems(null, " + JSON.stringify(state || {}) + ");'>Show more tasks</a></td></tr>";
+      break;
+    }
+    z = i + 1;
+    ptype = item.type.replace(/\s+/g, "");
+    cdate = new Date(item.created * 1000).toLocaleString();
+    lingos = (item.languages !== 'n/a' ? item.languages.split(",").join(", ") : "");
+    add = "";
+    if (state && state.admin) {
+      add = " <td><a href='/admin/close.lua?id=" + item.request_id + "'>Mark as done</a></td>";
+    }
+    item.description = item.description.replace(/\n/g, "<br/>").replace(hw_weburl, (function(_this) {
+      return function(a) {
+        return "<a href='" + a + "'>" + a + "</a>";
+      };
+    })(this));
+    tbl += "<tr style='cursor: pointer;' onclick=\"sw('details_" + i + "');\"><td width='68'><div class='itemNumber-yellow'>" + z + "</div><img title='" + item.type + "' style='float: left; width: 24px; height: 24px;' src='https://helpwanted.apache.org/images/icon_" + ptype + ".png'/></td>" + "<td>" + item.project + "</td>" + "<td style='text-align: left;'>" + item.title + "</td>" + "<td>" + lingos + "</td><td title='" + diff_explanation[parseInt(item.difficulty)] + "' style='text-align: left;'><img style='width: 20px; height: 20px; vertical-align: middle;' src='https://helpwanted.apache.org/images/level_" + (parseInt(item.difficulty) + 1) + ".png'/> " + diff[item.difficulty] + "</td><td>" + cdate + "</td>" + add + "</tr>";
+    tbl += "<tr style='display:none;' id='details_" + i + "'><td colspan='6'><b>Project:</b> " + item.project + "<br/><b>Requested by:</b> " + item.author + "@apache.org<br/><b>Created:</b> " + cdate + "<br/><b>Description:</b> <blockquote>" + item.description + "</blockquote><b>Further information: </b> <a href='" + item.url + "'>" + item.url + "</a><br/><input type='button' onclick='location.href=\"https://helpwanted.apache.org/task.html?" + item.request_id + "\";' value='I am interested in this'/></td></tr>";
+  }
+  tbl += "</table>";
+  obj.innerHTML += tbl;
+  if (location.href.search('admin') === -1) {
+    return location.hash = '#hwrtable';
+  }
+};
+
+fetchItems = function(languages, types, projects, sortBy, par) {
+  if (!languages) {
+    languages = [];
+  }
+  if (!types) {
+    types = [];
+  }
+  if (!projects) {
+    projects = [];
+  }
+  return fetch("https://helpwanted.apache.org/tasks.lua?lang=" + languages.join(",") + "&type=" + types.join(",") + "&project=" + projects.join(","), {
+    languages: languages,
+    types: types,
+    projects: projects,
+    sortBy: sortBy,
+    parentObject: par
+  }, displayItems);
+};
+
+fetchItemsAdmin = function() {
+  return fetch("https://helpwanted.apache.org/tasks.lua", {
+    admin: true
+  }, displayItems);
+};
+
+renderItem = function(json, state) {
+  var cdate, mlink, obj, overrides, p, rgba, rid;
+  p = pastels[parseInt(Math.random() * pastels.length)];
+  obj = get('item');
+  cdate = new Date(json.created * 1000).toDateString();
+  rid = json.request_id.substring(0, 8);
+  overrides = {
+    comdev: 'community',
+    whimsy: 'whimsical'
+  };
+  if (overrides[json.project]) {
+    json.project = overrides[json.project];
+  }
+  json.description = json.description.replace(/\n/g, "<br/>").replace(hw_weburl, (function(_this) {
+    return function(a) {
+      return "<a href='" + a + "'>" + a + "</a>";
+    };
+  })(this));
+  obj.innerHTML = "<h2>Task #" + state.substring(0, 8) + ": " + json.title + "</h2>";
+  mlink = "mailto:dev@" + json.project + ".apache.org?subject=" + escape("Help with task: " + json.title) + "&body=" + escape("I would like to help out with the task listed at https://helpwanted.apache.org/task.html?" + rid + "\n\n");
+  rgba = "rgba(" + p.r + "," + p.g + "," + p.b + ", 1)";
+  obj.innerHTML += "<div id='pickerparent' style='background: " + rgba + "; padding:12px;'><p style='text-align: left;'><b>Project: </b> " + json.project + "<br/>" + "<b>Created by:</b> " + json.author + "@apache.org<br/>" + "<b>Task added: </b>" + cdate + "<br/>" + "<b>Difficulty: </b> <img style='width: 16px; height: 16px; vertical-align: middle;' src='/images/level_" + (parseInt(json.difficulty) + 1) + ".png'/> " + diff[json.difficulty] + " - " + diff_explanation[parseInt(json.difficulty)] + "<br/>" + "<b>Task type:</b> " + types[json.type] + "<br/>" + (json.url && json.url.length > 10 ? "<b>Additional information:</b> <a href='" + json.url + "'>" + json.url + "</a><br/>" : "") + (json.estimate && json.estimate.length > 0 ? "<b>Estimated time to complete:</b> " + json.estimate : "") + (json.timeout && json.timeout > 0 ? "<b>Task expires:</b> " + new Date(json.timeout * 1000).toDateString() : "") + "<blockquote style='text-align: left;'><q>" + json.description + "</q></blockquote>
 " + "<br/></p>" + "<h3 style='text-align: left;'>How to help:</h3><p style='text-align: left;'>" + (json.curl && json.curl.length > 10 ? "<b>Contributor's guide for this project: </b><a href='" + json.curl + "'>" + json.curl + "</a><br/>" : "") + "If you want to help with this task, please get in touch with the project at: <a href=\"" + mlink + "\">dev@" + json.project + ".apache.org</a>!" + "<br/>You should also check out the additional information URL (if such is provided above) for more information.";
+  return "<br/>&nbsp;<br/>&nbsp;<br/></p></div>";
+};
+
+displayItem = function(id) {
+  return fetch("https://helpwanted.apache.org/tasks.lua?id=" + id, id, renderItem);
+};
+
+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,');
+};
+
+fetch = function(url, xstate, callback, snap) {
+  var xmlHttp;
+  xmlHttp = null;
+  if (window.XMLHttpRequest) {
+    xmlHttp = new XMLHttpRequest();
+  } else {
+    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
+  }
+  xmlHttp.open("GET", url, true);
+  xmlHttp.send(null);
+  return xmlHttp.onreadystatechange = function(state) {
+    var e, response;
+    if (xmlHttp.readyState === 4 && xmlHttp.status === 500) {
+      if (snap) {
+        snap(xstate);
+      }
+    }
+    if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
+      if (callback) {
+        try {
+          response = JSON.parse(xmlHttp.responseText);
+          if (response && response.loginRequired) {
+            location.href = "/login.html";
+            return;
+          }
+          return callback(response, xstate);
+        } catch (_error) {
+          e = _error;
+          return callback(JSON.parse(xmlHttp.responseText), xstate);
+        }
+      }
+    }
+  };
+};
+
+post = function(url, args, xstate, callback, snap) {
+  var ar, fdata, k, v, xmlHttp;
+  xmlHttp = null;
+  if (window.XMLHttpRequest) {
+    xmlHttp = new XMLHttpRequest();
+  } else {
+    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
+  }
+  xmlHttp.withCredentials = true;
+  ar = [];
+  for (k in args) {
+    v = args[k];
+    if (v && v !== "") {
+      ar.push(k + "=" + escape(v));
+    }
+  }
+  fdata = ar.join("&");
+  xmlHttp.open("POST", url, true);
+  xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+  xmlHttp.send(fdata);
+  return xmlHttp.onreadystatechange = function(state) {
+    var e, response;
+    if (xmlHttp.readyState === 4 && xmlHttp.status === 500) {
+      if (snap) {
+        snap(xstate);
+      }
+    }
+    if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
+      if (callback) {
+        try {
+          response = JSON.parse(xmlHttp.responseText);
+          return callback(response, xstate);
+        } catch (_error) {
+          e = _error;
+          return callback(JSON.parse(xmlHttp.responseText), xstate);
+        }
+      }
+    }
+  };
+};
+
+postJSON = function(url, json, xstate, callback, snap) {
+  var fdata, xmlHttp;
+  xmlHttp = null;
+  if (window.XMLHttpRequest) {
+    xmlHttp = new XMLHttpRequest();
+  } else {
+    xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
+  }
+  xmlHttp.withCredentials = true;
+  fdata = JSON.stringify(json);
+  xmlHttp.open("POST", url, true);
+  xmlHttp.setRequestHeader("Content-type", "application/json");
+  xmlHttp.send(fdata);
+  return xmlHttp.onreadystatechange = function(state) {
+    var e, response;
+    if (xmlHttp.readyState === 4 && xmlHttp.status === 500) {
+      if (snap) {
+        snap(xstate);
+      }
+    }
+    if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
+      if (callback) {
+        try {
+          response = JSON.parse(xmlHttp.responseText);
+          return callback(response, xstate);
+        } catch (_error) {
+          e = _error;
+          return callback(JSON.parse(xmlHttp.responseText), xstate);
+        }
+      }
+    }
+  };
+};
+
+mk = function(t, s, tt) {
+  var j, k, len, r, v;
+  r = document.createElement(t);
+  if (s) {
+    for (k in s) {
+      v = s[k];
+      if (v) {
+        r.setAttribute(k, v);
+      }
+    }
+  }
+  if (tt) {
+    if (typeof tt === "string") {
+      app(r, txt(tt));
+    } else {
+      if (isArray(tt)) {
+        for (j = 0, len = tt.length; j < len; j++) {
+          k = tt[j];
+          if (typeof k === "string") {
+            app(r, txt(k));
+          } else {
+            app(r, k);
+          }
+        }
+      } else {
+        app(r, tt);
+      }
+    }
+  }
+  return r;
+};
+
+app = function(a, b) {
+  var item, j, len, results;
+  if (isArray(b)) {
+    results = [];
+    for (j = 0, len = b.length; j < len; j++) {
+      item = b[j];
+      if (typeof item === "string") {
+        item = txt(item);
+      }
+      results.push(a.appendChild(item));
+    }
+    return results;
+  } else {
+    if (typeof b === "string") {
+      item = txt(b);
+      return a.appendChild(item);
+    } else {
+      return a.appendChild(b);
+    }
+  }
+};
+
+set = function(a, b, c) {
+  return a.setAttribute(b, c);
+};
+
+txt = function(a) {
+  return document.createTextNode(a);
+};
+
+get = function(a) {
+  return document.getElementById(a);
+};
+
+swi = function(obj) {
+  var switchery;
+  return switchery = new Switchery(obj, {
+    color: '#26B99A'
+  });
+};
+
+cog = function(div, size) {
+  var i, idiv;
+  if (size == null) {
+    size = 200;
+  }
+  idiv = document.createElement('div');
+  idiv.setAttribute("class", "icon");
+  idiv.setAttribute("style", "text-align: center; vertical-align: middle; height: 500px;");
+  i = document.createElement('i');
+  i.setAttribute("class", "fa fa-spin fa-cog");
+  i.setAttribute("style", "font-size: " + size + "pt !important; color: #AAB;");
+  idiv.appendChild(i);
+  idiv.appendChild(document.createElement('br'));
+  idiv.appendChild(document.createTextNode('Loading, hang on tight..!'));
+  div.innerHTML = "";
+  return div.appendChild(idiv);
+};
+
+isArray = function(value) {
+  return value && typeof value === 'object' && value instanceof Array && typeof value.length === 'number' && typeof value.splice === 'function' && !(value.propertyIsEnumerable('length'));
+};



Mime
View raw message