Changeset - 26ff31bb7878
[Not reviewed]
5 5 0
Ben Sturmfels (bsturmfels) - 2 years ago 2021-11-16 02:25:39
ben@sturm.com.au
progress bar: Drop JS, make mobile friendly, match design to new site.

The existing jQuery UI-based fundraising progress bar used a float layout,
making it hard to adapt for mobile use. Given that there is not interactivity,
I've dropped all the JS and switched to a flexbox layout. This works well
because the bar will stretch to fit the text rather than always maintaining its scale.
10 files changed with 48 insertions and 650 deletions:
0 comments (0 inline, 0 general)
www/conservancy/local_context_processors.py
Show inline comments
 
from datetime import datetime as DateTime
 
from pytz import utc as UTC
 

	
 
import conservancy.settings
 
from conservancy.apps.fundgoal.models import FundraisingGoal as FundraisingGoal
 

	
 
SITE_FUNDGOAL = 'cy2020-end-year-match'
 
SITE_FUNDGOAL = 'cy2021-end-year-match'
 
# FIXME: Move this information into the model.
 
FUNDGOAL_ENDTIMES = {
 
    # Noon UTC = the end of the previous day anywhere on Earth (AOE)
 
    'cy2018-end-year-match': DateTime(2019, 1, 16, 12, tzinfo=UTC),
 
    'cy2019-end-year-match': DateTime(2020, 1, 16, 12, tzinfo=UTC),
 
    'cy2020-end-year-match': DateTime(2021, 1, 16, 12, tzinfo=UTC),
 
    'cy2021-end-year-match': DateTime(2021, 1, 16, 12, tzinfo=UTC),
 
}
 

	
 
def fundgoal_lookup(fundraiser_sought):
 
    try:
 
        return FundraisingGoal.objects.get(fundraiser_code_name=fundraiser_sought)
 
    except FundraisingGoal.DoesNotExist:
 
        # we have no object!  do something
 
        return None
 

	
 
def sitefundraiser(request):
 
    return {
 
        'datetime_now': DateTime.now(UTC),
 
        'sitefundgoal': fundgoal_lookup(SITE_FUNDGOAL),
 
        'sitefundgoal_endtime': FUNDGOAL_ENDTIMES[SITE_FUNDGOAL],
 
    }
 

	
 
if conservancy.settings.FORCE_CANONICAL_HOSTNAME:
 
    _HOST_URL_VAR = {'host_url': 'https://' + conservancy.settings.FORCE_CANONICAL_HOSTNAME}
 
    def host_url(request):
 
        return _HOST_URL_VAR
 
else:
 
    def host_url(request):
 
        return {'host_url': request.build_absolute_uri('/').rstrip('/')}
www/conservancy/static/css/conservancy.css
Show inline comments
...
 
@@ -277,87 +277,86 @@ body > header {
 
#navbar.mobile li {
 
  display: block;
 
}
 
#navbar.mobile ul {
 
  padding-left: .5rem;
 
  padding-right: .5rem;
 
  display: block !important;
 
  position: relative !important;
 
  top: auto !important;
 
  left: 0 !important;
 
  right: auto !important;
 
  text-align: left !important;
 
}
 
#navbar.mobile > ul > li {
 
  padding-top: .5rem;
 
  padding-bottom: .5rem;
 
}
 
#navbar.mobile > ul > li {
 
  border-bottom: 1px solid rgba(255, 255, 255, 0.2);
 
}
 
#navbar.mobile li ul {
 
  margin-left: .5rem;
 
}
 

	
 
#progressbar {
 
    height: 1.8em;
 
.fundraiser-top-text {
 
  background: #F0FFB8;
 
  margin-top: -.5rem;
 
}
 

	
 
#progressbar .ui-widget-header {
 
    background: rgb(206, 31, 31);
 
.fundraiser-top-text p {
 
  font-size: 110%;
 
  font-style: italic;
 
  text-align: center;
 
}
 

	
 
#siteprogressbar .goalText {
 
    color: #557733;
 
    font-size: 10pt;
 
  color: #557733;
 
}
 
#siteprogressbar .soFarText {
 
    font-size: 10pt;
 
}
 
#siteprogressbar .progress {
 
    background: #577632;
 
}
 
@media all and (max-width: 600px) {
 
  .goalText {
 
      font-size: 8pt;
 
  }
 
  .soFarText {
 
      font-size: 8pt;
 
  }
 
  color: white;
 
}
 
#siteprogressbar .progress {
 
    background: #577632;
 
}
 
#siteprogressbar .middle-goal {
 
    background: #d0d0d0;
 
  background: linear-gradient(var(--khaki-green), #84a377, var(--khaki-green));
 
  padding-left: 0.5rem;
 
  padding-right: 0.5rem;
 
  border-top-left-radius: 16px;
 
  border-bottom-left-radius: 16px;
 
  border: 1px solid #3f4439;
 
}
 

	
 
#siteprogressbar .final-goal {
 
    background: #eeeeee;
 
  border-top-right-radius: 16px;
 
  border-bottom-right-radius: 16px;
 
  border: 1px solid #9bac88;
 
  border-left: none;
 
}
 

	
 
#fundraiser-percentage {
 
    text-align: center;
 
#siteprogressbar .progress.matched {
 
  border-top-right-radius: 16px;
 
  border-bottom-right-radius: 16px;
 
}
 
#siteprogressbar {
 
  background: linear-gradient(var(--washed-green), white, var(--washed-green));
 
  box-shadow: 1px 1px 1px rgba(0,0,0,0.2);
 
  line-height: 1.3;
 
  border-radius: 16px;
 
}
 

	
 
#container #mainContent {
 
    max-width: 50em;
 
    margin: 0;
 
    padding: 0;
 
    background: #ffffff;
 
    flex: 1 1 auto;
 
}
 
#container #sidebar {
 
  background-color: #e6eae1;
 
  padding: 1px 0.5rem 0.25rem;
 
  margin-bottom: 1rem;
 
}
 

	
 
@media screen and (min-width: 30em) {
 
  #container {
 
    display: flex;
 
  }
 
  #container #sidebar {
 
  flex: 1 0 15em;
 
    margin-right: 1rem;
 
  }
 
}
...
 
@@ -633,63 +632,48 @@ pre {
 
    font-size: 125%;
 
    font-weight: normal;
 
}
 

	
 
.supporter-type-selector a.supporter-type-selector-selected {
 
    font-size: 127%;
 
    font-weight: bold;
 
}
 

	
 
/* Make dl's ( such as for FAQ entries) look nice on screens, both big and small. */
 

	
 
dl {
 
    border: 3px double #ccc;
 
    padding: 0.5em;
 
}
 
dt {
 
    text-align: center;
 
    margin: 0em 1em 0.5em 0.5em;
 
    font-weight: bold;
 
    color: green; }
 
dd {
 
    margin: 0 0 1.5em 2em;
 
}
 

	
 
.fundraiser-top-text {
 
    background: #F0FFB8;
 
    padding: .2em .7em;
 
}
 
.fundraiser-top-text * {
 
    margin: .5em auto;
 
    max-width: 70em;
 
    width: 95%;
 
}
 
.fundraiser-top-text p {
 
    font-size: 110%;
 
    font-style: italic;
 
    text-align: center;
 
}
 

	
 
/* Fallback elements created by conservancy.js when no video source is
 
   supported. */
 
div.small-right, div.medium-right {
 
    border: thick solid #577632;
 
    padding: .3em;
 
    text-align: center;
 
}
 

	
 
.breadcrumbs {
 
  font-size: 14px;
 
  padding: 0.5rem 0 0;
 
}
 

	
 
.breadcrumbs, .breadcrumbs a {
 
  color: #777;
 
}
 
.breadcrumbs a {
 
  padding: 0 0.5em;
 
}
 
.breadcrumbs a:first-child {
 
  padding-left: 0;
 
}
 
.breadcrumbs span {
 
  padding-left: 0.5em;
www/conservancy/static/css/jquery-ui-1.8.22.custom.css
Show inline comments
 
deleted file
www/conservancy/static/css/jquery.ui.multiprogressbar.css
Show inline comments
 
deleted file
www/conservancy/static/js/conservancy.js
Show inline comments
 
/* Copyright (C) 2012-2013 Denver Gingerich,
 
** Copyright (C) 2013-2014 Bradley M. Kuhn,
 
** Copyright (C) 2016 Brett Smith.
 
** License: GPLv3-or-later
 
**  Find a copy of GPL at https://sfconservancy.org/GPLv3
 
*/
 

	
 
$(document).ready(function() {
 
    /* When the browser doesn't support any video source, replace it
 
       with the HTML inside the <video> element. */
 
    var showVideoInnerHTML = function(event) {
 
        var video = event.target.parentNode;
 
        var div = document.createElement('div');
 
        div.classList = video.classList;
 
        div.innerHTML = video.innerHTML;
 
        video.parentNode.replaceChild(div, video);
 
    }
 
    $('video').each(function(index, video) {
 
        $('source', video).last().on('error', showVideoInnerHTML);
 
    });
 

	
 
    /* Set up the fundraiser multiprogressbar near the top of each page. */
 
    var siteFinalGoal = $('span#site-fundraiser-final-goal').text();
 
    var noCommaSiteFinalGoal = parseInt(siteFinalGoal.replace(/,/g, ""));
 
    var siteMatchCount = $('span#site-fundraiser-match-count').text();
 
    var noCommaSiteMatchCount = parseInt(siteMatchCount.replace(/,/g, ""));
 
    if (! noCommaSiteMatchCount) {
 
        noCommaSiteMatchCount = "0";
 
    }
 
    var barParts = [{
 
        value: (noCommaSiteMatchCount / noCommaSiteFinalGoal) * 100,
 
        text: "$" + noCommaSiteMatchCount.toLocaleString() + " matched!",
 
        barClass: "progress",
 
        textClass: "soFarText",
 
    }];
 
    if (barParts[0].value < 100) {
 
        var matchesLeft = noCommaSiteFinalGoal - noCommaSiteMatchCount;
 
        barParts.push({
 
            value: 100,
 
            text: "$" + matchesLeft.toLocaleString() + " to go!",
 
            barClass: "final-goal",
 
            textClass: "goalText",
 
        });
 
    }
 
    $('#siteprogressbar').empty().multiprogressbar({parts: barParts});
 

	
 
    $('span#fundraiser-percentage').css({ 'color'        : 'green',
 
                                          'font-weight'  : 'bold',
 
                                          'float'        : 'right',
 
                                          'margin-right' : '40%',
 
                                          'margin-top'   : '2.5%',
 
                                          'text-align'   : 'inherit'});
 

	
 
    /* Set up donation form elements used across the whole site. */
 
    $('.toggle-content').hide();
 
    $('.toggle-control')
 
     .addClass('clickable')
 
     .bind('click', function() {
 
        var $control = $(this);
 
        var $parent = $control.parents('.toggle-unit');
 

	
 
        $parent.toggleClass('expanded');
 
        $parent.find('.toggle-content').slideToggle();
 

	
 
        // if control has HTML5 data attributes, use to update text
 
        if ($parent.hasClass('expanded')) {
 
            $control.html($control.attr('data-expanded-text'));
 
        } else {
 
            $control.html($control.attr('data-text'));
 
        }
 
    });
 
    $('a.donate-now')
 
      .addClass('clickable')
 
      .bind('click', function() {
 
        var $control = $('#donate-box');
 
        var $otherTextControl = $('.donate-sidebar');
 

	
 
        setTimeout(function() { $control.find('.toggle-content').slideUp(100);
 
                                $control.toggleClass('expanded');
 
                                $control.find('.toggle-content').slideDown(800).fadeOut(10);
 
                                $otherTextControl.find('.donate-box-highlight').fadeOut(100);
 
                              }, 300);
 
          setTimeout(function() { $control.find('.toggle-content').fadeIn(2000);
 
                                  $otherTextControl.find('.donate-box-highlight')
 
                                  .css({'font-weight': 'bold', 'font-size' : '110%' });
 
                                  $otherTextControl.find('.donate-box-highlight').fadeIn(10000);
 
                                }, 500);
 
    });
 

	
 
    $('input[name=on0]:radio').on('change', function(event, duration) {
 
        var $input = $(this);
 
        var wantShirt = $input.val() == "wantGiftYes";
 
        var $form = $input.parents('form').last();
 
        var $tShirtSelector = $('.t-shirt-size-selector', $form);
 
        $('input', $tShirtSelector).prop('disabled', wantShirt);
 
        $('input[name=no_shipping]', $form).val(wantShirt ? '2' : '0');
 
        if (wantShirt) {
 
            $tShirtSelector.slideDown(duration);
 
        } else {
 
            $tShirtSelector.slideUp(duration);
 
        }
 
    }).filter(':checked').trigger('change', 0);
 

	
 
    // Open mobile/search menu.
 
    $('#menu-icon').on('click', function(event) {
 
        $('#navbar').toggleClass('mobile');
 
    });
 
    $('#search-icon').on('click', function(event) {
 
        $('#navbar').toggleClass('mobile');
 
        $('#search-query').focus();
 
    });
 
});
www/conservancy/static/js/jquery-ui-1.8.22.custom.min.js
Show inline comments
 
deleted file
www/conservancy/static/js/jquery.outerhtml.js
Show inline comments
 
deleted file
www/conservancy/static/js/jquery.ui.multiprogressbar.js
Show inline comments
 
deleted file
www/conservancy/static/views.py
Show inline comments
 
import mimetypes
 
import os.path
 
from django.http import HttpResponse
 
from django.template.response import TemplateResponse
 

	
 
from conservancy.apps.fundgoal.models import FundraisingGoal
 
from conservancy.local_context_processors import fundgoal_lookup
 

	
 
STATIC_ROOT = os.path.abspath(os.path.dirname(__file__))
 
FILESYSTEM_ENCODING = 'utf-8'
 

	
 
def handler(request, errorcode):
 
    path = os.path.join('error', str(errorcode), 'index.html')
 
    fullpath = os.path.join(STATIC_ROOT, path)
 
    if not os.path.exists(fullpath):
 
        return HttpResponse("Internal error: " + path, status=int(errorcode))
 
    else:
 
        return TemplateResponse(request, path, status=int(errorcode))
 

	
 
def handler401(request):
 
    return handler(request, 401)
 

	
 
def handler403(request):
 
    return handler(request, 403)
 

	
 
def handler404(request):
 
    return handler(request, 404)
 

	
 
def handler500(request):
 
    return handler(request, 500)
www/conservancy/templates/base_conservancy.html
Show inline comments
 
{% load humanize %}
 
{% load subtract %}
 
{% load min %}
 
{% load static %}
 

	
 
<!DOCTYPE html>
 

	
 
<html lang="en" prefix="og: http://ogp.me/ns#">
 

	
 
  <head>
 
    <title>{% block title %}{% block subtitle %}{% endblock %}Software Freedom Conservancy{% endblock %}</title>
 
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
 
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
 
    <meta name="description" content="The Software Freedom Conservancy provides a non-profit home and services to Free, Libre and Open Source Software (FLOSS) projects." />
 
    <meta name="keywords" content="software, freedom, conservancy, open source, gnu, GNU, Open Source, Free and Open Source, Free and Open Source Software, FLOSS, FOSS, protect, protection, help, policy, linux, non-profit" />
 
    <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
 
    <link rel="stylesheet" type="text/css" href="{% static 'css/tachyons.css' %}"/>
 
    <link rel="stylesheet" type="text/css" media="screen" href="/css/conservancy.css" />
 
    <link rel="stylesheet" type="text/css" media="(min-width: 67em)" href="/css/conservancy-bigscreen.css" />
 
    <link rel="stylesheet" href="/css/jquery-ui-1.8.22.custom.css" />
 
    <link rel="stylesheet" href="/css/jquery.ui.multiprogressbar.css" />
 
    <script type="text/javascript" src="/js/jquery-1.7.2.js"></script>
 
    <script type="text/javascript" src="/js/jquery-ui-1.8.22.custom.min.js"></script>
 
    <script type="text/javascript" src="/js/jquery.outerhtml.js"></script>
 
    <script type="text/javascript" src="/js/jquery.ui.multiprogressbar.js"></script>
 
    <script type="text/javascript" src="/js/conservancy.js"></script>
 
    {% block head %}{% endblock %}
 
  </head>
 

	
 
  <body class="conservancy-{% block category %}{% endblock %}">
 
    <header>
 
      <div class="flex-ns center mw8">
 
        <div class="w-60-ns">
 
          <h1 id="conservancyheader" class="mt2 mt3-ns mb2 mb3-ns">
 
            <a href="/">
 
              <img src="{% static 'img/conservancy-header.svg' %}" alt="Software Freedom Conservancy" class="db center mh3-ns" />
 
            </a>
 
          </h1>
 
        </div>
 

	
 
        <div class="w-40-ns mt2 mt4-ns mb2 mb2-ns mh2 pt1 flex flex-wrap justify-center items-center">
 
          <span id="menu-icon" class="dn-ns ph2 pointer">
 
            <svg style="color: var(--orange); width: 30px; height: 30px;"><use href="{% static 'img/font_awesome.svg' %}#bars"></use></svg>
 
          </span>
 
          <a href="/donate/" class="f5 mh1 mv1 ph2 pv1 ttu b btn-orange">Donate</a>
 
          <a href="/sustainer/" class="f5 mh1 mv1 ph2 pv1 ttu b btn-orange">Join</a>
 
          <a href="/vizio/" class="f5 mh1 mv1 ph2 pv1 ttu b btn-orange">Vizio</a>
 
          <span id="search-icon" class="dib ph2 pointer">
 
            <svg style="color: var(--orange); width: 30px; height: 30px;"><use href="{% static 'img/font_awesome.svg' %}#search"></use></svg>
...
 
@@ -93,86 +88,91 @@
 

	
 
## From Local Context
 

	
 
* datetime_now: Current DateTime in UTC
 
* sitefundgoal: The current FundraisingGoal. Attributes:
 
  * fundraiser_goal_amount: The amount being matched
 
  * fundraiser_so_far_amount: The amount contributed so far
 
  * fundraiser_donation_count: The number of people who have contributed so far
 
  * fundraiser_donation_count_disclose_threshold: The number of new Sustainers that can be double-matched this fundraiser.
 
      (No, this name makes no sense. We're repurposing an existing model field for this new reason.)
 
* sitefundgoal_endtime: DateTime when sitefundgoal ends.
 

	
 
## Local convenience variables
 

	
 
* sitefundgoal_timeleft: TimeDelta for how much time remains in the current fundraiser
 
* this_match_goal: The amount being matched
 
* this_match_so_far: The amount contributed so far
 
* this_match_remaining: this_match_goal - this_match_so_far
 

	
 
{% endcomment %}
 

	
 
{% if sitefundgoal and sitefundgoal.fundraiser_so_far_amount and datetime_now < sitefundgoal_endtime %}
 
{% with this_match_goal=sitefundgoal.fundraiser_goal_amount this_match_so_far=sitefundgoal.fundraiser_so_far_amount %}
 
{% with this_match_remaining=this_match_goal|subtract:this_match_so_far sitefundgoal_timeleft=sitefundgoal_endtime|subtract:datetime_now %}
 
    <div class="fundraiser-top-text">
 
      <p>
 
    <div class="fundraiser-top-text ph2 ph3-ns pt2 pb3">
 
      <div class="mw8 center ph2 ph4-ns">
 
      <div class="mt2 mb3 tc">
 
        {% if this_match_remaining <= 0 %}
 
          Thanks to {{ sitegoal.fundraiser_donation_count|intcomma }} Sustainers we earned our full match!
 
          Help us go further to stand up for software freedom &mdash; <a href="/sustainer">sign up now</a>!
 
        {% else %}
 
          {% if sitefundgoal_timeleft.total_seconds <= 0 %}
 
            The
 
          {% elif sitefundgoal_timeleft.days == 0 %}
 
            Through today only, the
 
          {% elif sitefundgoal_timeleft.days == 1 %}
 
            Through tomorrow only, the
 
          {% elif sitefundgoal_timeleft.days < 14 %}
 
            For only {{ sitefundgoal_timeleft.days }} more days, the
 
          {% else %}
 
            Until January 15, the
 
          {% endif %}
 
        next ${{ this_match_remaining|floatformat:0|intcomma }} of <a href="/sustainer/">support we receive</a> will be matched!
 

	
 
        {% endif %}
 
      </p>
 
      </div>
 

	
 
{% if sitefundgoal.fundraiser_so_far_amount %}
 
<div id="siteprogressbar">
 
<a href="/sustainer">
 
  We've matched
 
<a href="/sustainer/" style="text-decoration: none !important">
 
<div id="siteprogressbar" class="flex items-stretch w-100">
 
  {% if this_match_remaining <= 0 %}
 
  $<span id="site-fundraiser-match-count">{{ this_match_goal|intcomma }}</span>
 
    <div class="progress matched tc pv1 b dt" style="flex-basis: {{ this_match_so_far }}px">
 
      <span id="site-fundraiser-match-count" class="soFarText dtc v-mid">${{ this_match_goal|floatformat:0|intcomma }} matched!</span>
 
    </div>
 
  {% else %}
 
  $<span id="site-fundraiser-match-count">{{ this_match_so_far|intcomma }}</span>
 
  {% endif %}
 
  of
 
  $<span id="site-fundraiser-final-goal">{{ this_match_goal|intcomma }}</span>
 
  so far!
 
</a>
 
    <div class="progress tc pv1 b dt" style="flex-basis: {{ this_match_so_far }}px">
 
      <span id="site-fundraiser-match-count" class="soFarText dtc v-mid">${{ this_match_so_far|floatformat:0|intcomma }} matched!</span>
 
    </div>
 
    <div class="final-goal tc pv1 b dt" style="flex-basis: {{ this_match_remaining }}px">
 
      <span id="site-fundraiser-final-goal" class="goalText dtc v-mid">${{ this_match_remaining|floatformat:0|intcomma }} to go!</span>
 
    </div>
 
    {% endif %}
 
</div>
 
</a>
 
{% endif %}
 

	
 
</div>
 
</div>
 
{% endwith %}
 
{% endwith %}
 
{% endif %}
 

	
 
    <div class="mw8 center ph2 ph3-ns">
 
      {% block outercontent %}<div id="mainContent"> {% block content %}{% endblock %}</div>{% endblock %}
 
    </div>
 

	
 
    <div id="conservancyfooter" class="mt4 pt3 ph3 bg-light-gray">
 
      <p>Connect with Conservancy on
 
        <a href="https://mastodon.technology/@conservancy">Mastodon</a>,
 
        <a href="https://twitter.com/conservancy">Twitter</a>,
 
        <a href="https://www.facebook.com/SoftwareFreedomConservancy/">Facebook</a>,
 
        and <a href="https://www.youtube.com/channel/UCUEeuNvX2UyTTyTYXR9dm_A">YouTube</a>.</p>
 

	
 
      <p><a href="/">Main Page</a> | <a href="/about/contact/">Contact</a> | <a href="/sponsors/">Sponsors</a> | <a href="/privacy-policy/">Privacy Policy</a> | <a href="/feeds/omnibus/">RSS Feed</a></p>
 
      <p>Our privacy policy was last updated <strong>22 December 2020</strong>.</p>
 

	
 
      <p class="copyright_info">
 
      <a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a>
 
      <br />This page, and all contents herein, unless a license is otherwise
 
      specified, are licensed under a
 
      <a rel="license" href="https://creativecommons.org/licenses/by-sa/4.0">Creative
0 comments (0 inline, 0 general)