• in

    Portland Women's History Trail in Apple AppStore

    Today iOS version of the Portland Women's History Trail went live in the Apple AppStore.

    iOS app in the AppStore

    The web version has been live for some time. The Android version went live just over a week ago. Now, the final version is live for iOS users to find easily in the Apple AppStore.

    The trail was originally created by Eileen Eagan and Polly Welts Kaufman in Portland (Maine). The app is built using jQuery Mobile and jekyll to create a complete HTML5/CSS3/JavaScript application. To read more about the application, check the related posts below.

  • in

    Portland Women's History Trail on Google play Store

    Late last week the Android version of the Portland Women's History Trail went live in the Google play.

    Android app on Google play

    The web version has been live for some time and the iOS version is currently waiting for review by Apple.

    The trail was originally created by Eileen Eagan and Polly Welts Kaufman in Portland (Maine). The app is built using jQuery Mobile and jekyll to create a complete HTML5/CSS3/JavaScript application. To read more about the application, check the related posts below.

    Here's a few screenshots to whet your appetite:

    screen capture   screen capture

  • in

    Cordova Apps and Offline Operation

    There are a number of quirky tasks to complete when you get ready to publish an app in either the Apple App Store or the Google Play Store. They are not the type of thing I, as a developer, usually think about when starting a project. As the Portland Women's History Trail gets nearer to "submission ready," I've steadily chopped away at that list, but the first major task was making a fundamentally HTML/CSS/JavaScript application work when bundled and offline took a bit of research and ultimately, doing things the "right way."

    The app is developed in Cordova and uses several frameworks like jQuery, jQuery Mobile, Google Maps, Google Analytics, and a few Google Fonts. When the app is run and the user has network connectivity, things work as expected. The problem comes when the app is bundled to a native app with Cordova and launched when offline.

    jQuery and jQuery Mobile

    jQuery and jQuery Mobile are easy problems to solve. You can just bundle them with the app and fetch them locally. This has the benefit of faster loading even when the device is online. Simple, done, still broken.

    Google Fonts

    The graphic design of the app called for two specific, freely available, Google Fonts; Roboto and PT Serif Caption. Normally these are simply @imported in your site's CSS file:

    @import url(https://fonts.googleapis.com/css?family=Roboto);
    @import url(https://fonts.googleapis.com/css?family=PT+Serif+Caption);
    

    As you might suspect from the rest of this article, this causes problems when you are offline or have limited bandwidth. To solve this, the font files need to be served from the local system. The added complexity is that the @import does not refer to the font file directly, but to a CSS @font-face specification that in-turn refers to the font files. We thus, have a two part solution.

    First, include the following in the CSS, replacing the @import:

    @font-face {
        font-family: 'Roboto';
        font-style: normal;
        font-weight: 400;
        src: local('Roboto'), local('Roboto-Regular'), url("css/fonts/Roboto.ttf"),     url(https://fonts.gstatic.com/s/roboto/v15/zN7GBFwfMP4uA6AR0HCoLQ.ttf) format('truetype');
    }
    
    @font-face {
        font-family: 'PT Serif Caption';
        font-style: normal;
        font-weight: 400;
        src: local('PT Serif Caption'), local('PTSerif-Caption'), url("css/fonts/PTSerif-Caption.ttf"), url(https://fonts.gstatic.com/s/ptserifcaption/v8/7xkFOeTxxO1GMC1suOUYWVRF2Zew_pgh0xNsNWxurqo.ttf) format('truetype');
    }
    

    This specifies the fonts in question and gives list of locations to try and find them. Note the order, we will look local() first, then the local-served url(), and finally if those fail the hosted url(). The last one should not be needed, but I left it in for reference. That is, the local-hosted should satisfy the request.

    Second you need to download the actual font files (.ttf in this case) and place them into the correct location (css/fonts for this project) so the HTML engine can find them at runtime.

    Problem solved. And really, not that bad of a solution. Like jQuery and jQuery Mobile this should speed things up a bit as the fonts don't have to traverse the Internet to get to the device. Additionally it will help those that are bandwidth-impaired.

    Google Maps

    Google Maps is where the main problem occurs. If loaded as part of the page header when offline, it silently fails to load and maps are not displayed. Nor is there any sort of error message displayed.

    <script src="https://maps.googleapis.com/maps/api/jssensor=true"></script>
    

    The easiest method I found to solve this is to use the functionality that Cordova provides in the device plugin. This plugin sends messages to us when the devices is initially ready, when it comes online, and when returns from the background. You can use these along with programmatically loading the Google Maps script and viola! Problem solved! Thank you to Coding With Spike and Loading Google Maps in Cordova the Right Way. Note the code below is an excerpt of the application's code meant to convey the idea not meant to be runnable as-is.

    function loadMapsApi() {
        //console.log("loadMapsApi()");
        if (navigator.connection.type === Connection.NONE) {
            return;
        }
    
        if (typeof google == "undefined" || typeof google.maps == "undefined") {
            //console.log("Maps API not loaded");
            $.getScript('https://maps.google.com/maps/api/js?callback=onMapsApiLoaded');
        } else {
            //console.log("Maps API already loaded");
            setTimeout(function(){onMapsApiLoaded();});
        }
    }
    
    (function(global) {
        "use strict";
        //console.log("main.js: global function()");
    
        $( "[data-role='navbar']" ).navbar();
        $( "[data-role='header'], [data-role='footer']" ).toolbar();
    
        function onDeviceReady () {
            //console.log("onDeviceReady()");
        }
    
        function onOnline () {
            //console.log("onOnline()");
            if (map == null && $.mobile.activePage.attr('id') == "map") {
                loadMap();
                showMap();
            }
        }
    
        function onResume () {
            //console.log("onResume()");
            if (map == null && $.mobile.activePage.attr('id') == "map") {
                loadMap();
                showMap();
            }
        }
    
        document.addEventListener("online", onOnline, false);
        document.addEventListener("resume", onResume, false);
        document.addEventListener("deviceready", onDeviceReady, false);
    })(window);
    

    Google Analytics

    Google Analytics caused me a few problems as well. Not by being off-line, but by trying to determine where to fetch analytics.js from. You can see this in the base code, provided by Google:

    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
    

    A helpful, but not conclusive suggestion was to include the new checkProtocolTask option:

    ga('set', 'checkProtocolTask', null); // Disable file protocol checking.
    

    Ultimately, having a different code path for Cordova and non-Cordova builds got the job done:

    {% if site.cordova %}
        var analyticsURL = "{{ site_root }}js/analytics.js"; // local for Cordova
    {% else %}
        var analyticsURL = "//www.google-analytics.com/analytics.js";
    {% endif %}
    (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
    (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
    m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document, 'script', analyticsURL, 'ga');
    

    Ready for Offline

    Now the app is ready native bundling and will work nicely online and offline. The changes for jQuery, jQuery Mobile and Google Fonts are relatively simple -- host and load them locally. The changes for Google Fonts is similar but with a twist -- two-step host and load them locally. Google Analytics involved a minor code change dependent on the deployed platform (web vs native). And the changes for Google Maps is the most complex. All of the changes together provide a more robust application that degrades gracefully when bandwidth is limited or the device goes offline.

    Now, back to my list of getting the app ready for publication in the App Stores.

  • in

    Ham Radio at the University of Scouting 2015

    I had the opportunity to present at this year's University of Scouting (Pine Tree Council, 2015) on Ham Radio. The class was well attended and there was a wealth of information to share -- not all of which could be done in the short hour we had allocated to us. The attendees were mostly unlicensed Scouts and adults looking to understand what would be involved in earning the Radio Merit Badge and becoming a licensed Ham.

    There are many aspects of Amateur Radio (Ham Radio) and we won't be able to cover them all here either -- this is just a summary and recap of the presentation. With that in mind, lets jump right in.

    Mind Map of Presentation

    You can see the full Mind Map of the presentation by clicking on the summary image.

    Introduction to Ham Radio

    Go listen to some morse code. Really. Listen to it. Can you make out the dots and dashes? Notice how you can hear it even when there's a lot of noise. Notice how well those dots and dashes cut through nearly anything. Continuous Wave (CW) is mighty good at working in the most undesirable situations. There's a reason it has hung around for so long and now you know why, it works!

    Ham Radio is not about morse code though, it is much more. In fact morse code is not even required anymore to get a Ham License. There's only a multiple choice exam, given by other volunteers. Sounds a lot like Scouting doesn't it, volunteers. There's even achievements, recognition, membership cards, and badges (patches)!

    On The Air

    Getting on the air is really the purpose of ham radio. It's like the putting the "out" in scOUTing. It's the thing, the purpose. At the start you listened to some morse code. Along with code, there are numerous other ways to communicate over the air; voice, video, and data.

    Voice, or phone as some call it, is what you would expect, people talking. There are a few ways to communicate voice or sounds, two you may already know; AM and FM. These are the same technologies that your regular radios use. They are, however, a bit inefficient with use of the radio waves. To get more people talking in the same space we can use single side band (SSB). SSB is effectively half of an AM signal. This half of the signal is enough for us to understand what's being said. It also gives a distinct sound to the conversations that you'll likely not hear anywhere else.

    There's plenty of data going over the air as well. In fact there are many networks similar to the Internet that work without the land-based lines the Internet runs on. The long history and variety of digital modes is quite a bit more than we can go into here, suffice to say that much of the modern Internet has it's roots in how hams have used digital over the air.

    Moving away from communication modes, ham radio also gives us the ability to communicate to folks locally and quite far away. One of my first DX (distant) communications was from a mountain ridge in Maine to New Zealand. Today's Internet may make this seem like a trivial contact, as with it we can talk to anyone, anywhere, nearly anytime. With radio however, these windows that allow us to talk to different places move during the day, night, and with the solar cycles. So talking to someone in New Zealand from Maine might only be possible for an hour right around dusk. There's much more of a challenge to it.

    When we put this distant communication in the context of Scouting we can easily see where the Eagle required merit badge Citizenship in the World can factor in. One of the requirements is to talk to a scout in another country about scouting. Wouldn't it be fun to do that on a random contact by radio where you both share in the excitement!

    QSL Cards

    After you've talked to a few folks around the world, you might want to keep a log to remember what you've done. Logs aren't required anymore, though many hams, myself included, still keep them. In addition, you can send and request QSL cards from people you have made contact with. These QSL cards look like a postcard and have on them the details of your contact. Things like callsigns, time, date, frequency, and how good (or bad) your signal was.

    Many hams take pride in a well designed QSL card and include photos of their station, their loved ones, or just some fun radio related items. Some cards that are sought after are from special event stations and locations. A few recent ones I received commemorated Veterans Day in the US and Christmas in Finland. The variety of cards and locations you can get them from makes for a very fun collection. And, again, if you're of the Scouting mind, you can use them to work on your Collecting merit badge.

    Special Events and Contests

    Wait, special event QSL cards? There are special events? Why yes there are all kinds of special events for hams and scouts.

    The one I've found the most fun is the annual Field Day. Field Day is held worldwide in June each year and is intended to practice emergency preparedness for ham operators. By utilizing emergency power, typically outside their regular operating locations, hams try to make as many contacts in 24-hours with others doing the same, as they can. The goal is to demonstrate they are ready to operate and exchange critical information during an emergency. Conveniently, scouts can also use parts of this in relation to their Emergency Preparedness merit badge work.

    Field day is only one of the many special events. One geared directly towards scouting is the annual Jamboree On The Air (JOTA) held each October. This event is, like field day, a contest to get as many contacts as you can in 24-hours. Here though the goal is to make contacts with scouts! Along with all the contacts you can make there's even a patch you can get.

    Awards and Recognition

    QSL cards and patches for ham radio are only two of the types of recognition for special events and contests. Some hams take pride in contacting all 50 of the United States to earn their Worked All States (WAS) award. Others feel the DX Century Club (DXCC) is the primer award for working 100 distinct countries. There are numerous other awards and endorsements that a ham can earn. These include working all "zones" as defined by the International Telecommunications Union, working all the European countries, all the Canadian provinces, and many, many more.

    Scouts and Radio

    There are a lot of aspects of ham radio that relate to Scouting and Scout merit badges, none is as direct as the Radio merit badge. It specifically has amateur radio as one of it's completion paths. Learning and understanding ham radio is a requirement of that path. The merit badge is not the only connection. There is an Amateur Radio Operator shoulder patch, that can be worn by any licensed ham, and a Morse Code interpreter strip, that can be worn by any Scout or Scouter who demonstrates proficiency in Morse Code. Beyond those two the Boy Scouts of America recognizes the American Radio Relay League (ARRL) as a community organization and offers an award (and knot) for those who make "...a significant contribution to providing Scouts with a memorable and valuable Amateur Radio experience."

    One of these memorable experiences is the special amateur radio station, K2BSA, that scouts and scouters can contact on the air and get a special QSL card. Even more exciting is that you can operate K2BSA. Then you are the special event!

    Becoming a Ham

    Becoming a ham is easier than ever. There's no Morse Code requirement, it was phased out years ago. There are three levels of license in the Amateur Radio Service; technician, general, and amateur extra. Each level has an examination that is administered by volunteers, certified by a volunteer examiner coordinator. The ARRL is one such coordinator. The exams are open to all and offered at various times across the country.

    The exams themselves are straightforward. The questions and answers are public and published for all to read and review. They cover general technical (electronics and radio) knowledge, operating procedures, and regulations (where you can transmit). Weighing in at 30 questions for the Technician with only 26 correct required to pass you could easily be on your way to your first license in a few days of studying.

    When you get there, to your first license, there's a lot you can do. So go, get out there, get on the air. Enjoy, talk, collect, prepare, get the certificates, the patches. Most importantly though, have fun!

    73, N1SH

  • in

    Fixing Absolute Paths, From Jekyll to Cordova

    Jekyll has really been a great tool to develop a site with. It allows me to take a bunch of content and lay it into templates to generate a static website, with dynamic-like content, that can be served from nearly anywhere. With jQuery Mobile and some JavaScript thrown in, an entire application is easily born. This was the approach I took with the Portland Women's History Trail project. However, my troubles began when working to bundle the HTML5/CSS/JavaScript into an native app with Cordova.

    Jekyll is designed to develop hosted websites, not applications to be deployed with Cordova. When the site is compiled, internal links are generated as is. Meaning no special processing is done to locate things relatively. There is some help for sites not hosted a the root of their domain, in using the site.* configuration, but this is not enough. In order for the site to work when packaged in Cordova all the links must be relative or you have to work out some method or patching them up to the internal location at runtime. Not fun.

    Using a site map, we can look at a specific example.

        wwwroot/                                    
          index.html                                
          trails.html                               
          places.html                               
          map.html                                  
          places/                                   
            congress-street/                        
              c01-curtis-and-sons.html              
              c02-maine-sports-hall-of-fame.html    
              ...                                   
            munjoy-hill/                            
              m01-native-american-site.html         
              m02-peaks-island.html                 
              ...                                   
            ...                                     
          trails/                                   
            congress-street.html                    
            munjoy-hill.html                        
            ...                                     
          photos/                                   
            ...                                     
          css                                       
            ...                                     
          js                                        
            ...                                     
    

    In the top-level pages (index.html, trails.html, places.html, and map.html) all is well. Their links to each other and to deeper pages all work with the default Jekyll configuration. That is a link in trails.html referring to the Congress Street Trail gets constructed as trails/congress-street.html.

    The problem crops up in those deeper pages. Using the Congress Street Trail as the example. In trails/congress-street.html the link for the first site is constructed as places/congress-street/c01-curtis-and-sons.html. Phoey. The URL is relative to the top-level of the site.

    Jekyll has a setting in the configuration files to try and accommodate relocation of the site root. This however does not solve the problem as nested pages need to know their relative depth from the site root to make this work.

    Not wanting to complicate the layout (template) pages, I took a stab at solving this with a simple Python script. The essence of it was, for each line in each .html, .css, and .js file

    line = re.sub(r"(href=)(['\"])/(.*?)\2", relrepl, line)
    line = re.sub(r"(src=)(['\"])/(.*?)\2", relrepl, line)
    

    where replrepl is defined as:

    def relrepl(match):
        if depth:
            fix = '../' * depth
        else:
            fix = ''
    
        return match.group(1) + match.group(2) + fix + match.group(3) + match.group(2)
    

    I was quite content with this quaint post-processing approach for about 8 hours. Then it hit me, I was fighting the framework that Jekyll provides. I almost always find that if things seem hard or impossible to do within the framework you are working with, your approach is wrong. You are not using the framework how it was designed, you are fighting it.

    The in-framework approach is to use Liquid tags to figure out at what depth the page is and add that many ../'s to the front of problematic URLs. Actually it's cleaner to create a variable (e.g. site_root) that has the right number of ../'s and prepend that where needed. Unfortunately the variable scope in Liquid is very local, so the variable creation code needs to go at the beginning of any file that will use it. Here's a specific example, the project's includable page footer, footer.inc

    {% capture my_lvl %}{{ page.url | split:'/' | size }}{% endcapture %}{% capture site_root %}{% for i in (3..my_lvl) %}../{% endfor %}{% endcapture %}
    <div data-id="footer" data-role="footer" data-position="fixed" data-tap-toggle="true" data-theme="a">
         <div data-role="navbar">
              <ul>
                   <li><a data-tab="trails" href="{{ site_root }}trails.html" data-icon="home" data-transition="fade">Trails</a></li>
                   <li><a data-tab="map" href="{{ site_root }}map.html" data-icon="location" data-transition="fade">Map</a></li>
                   <li><a data-tab="places" href="{{ site_root }}places.html" data-icon="bullets" data-transition="fade">Sites</a></li>
              </ul>
         </div>
    </div>
    

    Note that all hard-coded URLs are prefix-less; they lack a / at their start, making them relative. This is contrary to many of the framework examples. In fact, Jekyll generates page.url with the / prefix. For links to be relative that needs to be stripped off. The following does just that, on a page that lists all the sites associated with a particular trail.

    <ul id="place-list" data-role="listview">
        {% assign trail_places = site.places | where:"trail", page.trail %}
         {% for place in trail_places %}
              <li><a href="{{ place.url | remove_first:'/' | prepend:site_root }}">
                   {{ place.title }}</a>
              </li>
         {% endfor %}
    </ul>
    

    Both of these approaches work in either a web-hosted and bundled application. They both make all the internal links relative to the current page. They both appear to function the same. However, the Python version uses a post-processing step that makes the generated product different in the web-hosted and bundled applications. This could easily introduce bugs or currently unseen functional differences that would be difficult to track down in the future. Working with rather than against the framework is the right choice.

  • in

    Maine Campus Crush

    Maine Campus Crush is a tile-matching puzzle video game. It is also a challenge game between the University of Maine System campuses. Choose your team and play for the highest scores!

    Leaderboard

    • Loading Leaderboard...
    • 0

      Undeclared

    • 0

      Moose

    • 0

      Beavers

    • 0

      Bengals

    • 0

      Owls

    • 0

      Black Bears

    • 0

      Huskies

    • 0

      Clippers

    • 0

      System

    Gameplay

    The objective of this game is to swap one campus mascot tile with an adjacent tile to form a horizontal or vertical chain of three or more tiles of the same color. Bonus points are given when chains of more than three identical tiles are formed and when two chains are formed in one swap. Tiles disappear when chains are formed and new tiles fall from the top to fill in gaps. Sometimes chain reactions, called cascades, are triggered, where chains are formed by the falling tiles. Cascades are not yet awarded with bonus points. There are two variations of the game to choose from.

    Although normally the player gets only three in a row for tiles, sometimes they can get four or five in a row. And in rare conditions, they can get six, seven, and even eight in a row! You get more points for each additional tile that matches.

    The game is over when no more moves can be performed. The score is then compared against others from your campus and, if high enough, ranked on the leaderboard.

    Background

    The game is heavily based on Bejewled

  • in

    Portland Women's History Trail

    A mobile, web-based version of the Portland Women's History Trail originally created by Eileen Eagan and Polly Welts Kaufman in Portland (Maine). The app is built using jQuery Mobile and jekyll to create a complete HTML5/CSS3/JavaScript application.

    screen capture   screen capture

    Visit the Trail on the Web

    Android app on Google play

    The content is all markdown and incredibly simple to maintain. Jekyll is used to convert the content to HTML which is then laid into templates for each content type (trail, sites, etc.). With the templates jekyll spits out a completed "application."

    The application is still under development so there are still quirks that need to be sorted out. Navigation within a trail and site is not broken but close. The current design is a bottom tabbed (aka iOS) interface and then has a navigation within each tab.

    The prototype was relatively easy to get up and running in a few hours. It has the features and ideas that make it easy to show others, including non-technical folks. It gives the feel of the design and shows a possible implementation. And, as with any prototype, it needs work.

subscribe via RSS