CraftedPlaces.com

The latest of a series of portfolio sites, a project unto itself.

Why does CraftedPlaces.com have a page about itself?

I've done a lot of good work as an ActionScript/JavaScript developer over the last fifteen years. A few years ago, the sexiest projects that really showcased my work as a front end developer were all Flash (ActionScript) pieces, however, and that is not only very out of fashion these days, but will be discontinued soon. There are a couple of projects in particular that I did while at Somnio that I'm really proud of because I played a large role in bringing them to fruition. One of them - IBM CloudQuest - was even highlighted on their site for a while!

But even though the Flash platform has yet to find a real replacement in the plethora of HTML5/JavaScript libraries and toolkits that are currently being offered, ActionScript developers are no longer in great demand due to the legitimate desire that clients have for their content to reach folks on mobile devices (tablets and phones).

Through the years, I've done a number of portfolio sites to showcase my work. In each case, the site itself was quirky and interesting enough to want to draw attention to its details. My first version of ren-man.net (domain name now defunct) featured these really cool SVG-based line drawing animations for each section of the site that I actually miss! It also had a variable-width content section many years before responsive design became popular.

I've decided to showcase this current site because, having started from an initializr.com responsive template, I've done all the design and layout and code.

That left me reaching for examples of my work as a JavaScript front end developer. Now, I had done a lot in that department, e.g. troubleshooting HTML5/JS interactive pieces on tablets as well as helping with the then-current version of beehiveid.com, but none of those projects seemed worthy of its own page on this portfolio site. CraftedPlaces.com, however, shows off a little of everything that I can do, combining design and development.

June 2019 edit: I was frustrated by the site's lack of indexability (i.e., being invisible to Google's crawlers) so I've taken away the SPA functionality and “flattened” the site by making it into a bunch of static PHP pages. Read more at the bottom.

Features

Here's what's going on under the hood.

Responsive Design

As I've already mentioned, I started from initializr.com's responsive template, but I've made so many changes that it's fair to call this design my own! I redid the tabs to look like tabs and chose the color scheme. I'm not only using media queries to adjust the CSS according to the device screen width — I've added code which detects touch devices, because there is no longer a strict correlation between screen width and touch-enablement (for lack of a better word).

Currently, it's difficult to use JavaScript to detect touch devices. The method typically used involves checking for the support of touch events within the browser, but that fails (produces false positives) because the browsers often indicate support for those events even if no touch screen is attached to the device. And current touch screens don't support 'hover' events so developers have to dance delicately around the way the varying operating systems (I'm looking at you, iOS) deal with them.

My site listens for the first actually triggering of a touchstart event and then alters the backbone model, with the views responding accordingly. What that means to the users with a phone is the the nav will abruptly change (once) to make this adjustment, presenting submenu items which float near the top of the page.  Tablets have screens too wide to observe this.

Single Page Backbone Application

One thing that all of my portfolio sites have had in common has been the avoidance of page reloading. Somewhere along the line I decided that I didn't like the 'blink' which can occur when navigating to a new HTML page and wanted to facilitate smoother transitions. So, whether done in Flash or HTML and AJAX, my sites have featured pages which

  • load once, not wasting time or bandwidth reloading the same header over and over,
  • don't blink as new content is loaded into the main content area,
  • all the while mainting bookmarkable pages and deep linking (yes, even the Flash version).

The developers at Enspire chose Backbone JS as the foundation for their HTML5 products and I've followed suit by creating my website as a single page backbone application (not to be confused with single-page brochure sites which have all of their content on one tall, scrolling page).

A single-page application (SPA), also known as single-page interface (SPI), is a web application or web site that fits on a single web page with the goal of providing a more fluid user experience akin to a desktop application.

Page Background

So, one thing I can do because my site is a single-page application is choose a smooth transition of the background texture which would be difficult or impossible otherwise. At first, my idea was to use some sort of canvas-based displacement filter animate a sort of 'wave ripple' which would emanate from the tabs when the user clicked on them, but that was going to be way more trouble than it was worth.

My second thought was to use a particle engine and cause a new section's background texture to flow visually out of the tabs after clicking on them. I actually built this out (using the proton HTML5 particle engine) but wasn't pleased with the effect. Even after adjusting it to just cause the bg images to 'pop up' over the background, I found that performance was varying too much from one browser to the next.

I finally landed on the DOM-oriented solution you can now see on devices with screen widths greater than 850 pixels. The background only changes when navigating between sections of the site, and takes its sweet time doing so.  The old background stays in place until after the new content has loaded.

June 2019 edit: I removed this particle effect from the flattened version of the site. It was a gratuitous means of displaying the SPA functionality which is no longer in place.

Galleries

I started with the fancybox jquery 'lightbox' library to build the custom photo/video galleries you seen in place throughout the site. Originally, I used the videojs library to display HTML5 video and jumped through a bunch of hoops to make both the images and videos responsive (i.e., changing size appropriately while keeping aspect ratios constant). The newer version of fancybox handles the responsive HTML5 video just fine on its own. Thumbnails are automatically overlaid with little arrows indicating video content and the word 'anigif' for animated GIFs.

Gallery content can be browsed in many ways:

  • clicking to the right or left of the image/video,
  • using the mouse scroll wheel,
  • using the arrow keys,
  • “grabbing” images and sliding them, or
  • starting a slideshow with the play button to the upper right.

Read More sections

The initializr.com template that I started with featured a two-column layout (main article on the left, "aside" content on the right) that I retained. I'm using the aside area to display images and to contain special "read more" sections which expand/contract when clicked. The callout shape is done by CSS alone, which is fun. I'm reusing the same code to produce the little 'help' popouts (designated with a '?') for the gallery sections. Their behavior is such that they stay open unless clicked or unless another callout is opened.

gratuitous flower imageClick anywhere on this callout to close it.

After a while I realized that some of the "read more" sections had too much content in them and needed to be displayed full-width so I made provision for that as well.

Workflow — Batch Files & Python Scripts

I've written a number of scripts which help automate my workflow.

preprocess.py

I'm a beginner when it comes to Python. I know enough to automate some processes for myself, but I have to look up things like list comprehensions every time I use them, and I'm sure that seasoned users would not describe my approaches as 'pythonic.' 

This script accomplishes several things at once.  

  • I decided to implement my source code by using proprietary markup (kind of like angular, but not going to change anyone's life). Sections like the "read more" asides are encompassed in <readmore> tags, which the script converts into the actual <div> tags. Likewise, the photo/video galleries are marked up within <gallery> tags, with only <img> tags pointing to the thumbnails. The script does a search/replace to put all the necessary markup there.
  • The script reads the file's "last modified" date to automatically insert that info into the web page.
  • The script only overwrites a file if something has changed. This way, the modified timestamp on all files is not reset every time the script is run, and my FTP syncing software can be used to only upload those files which are new or newly modified.
  • Two different sitemap.xml files are automatically created, one for Google's use, and one with extra <label> nodes (which Google can't accept) which my site's HeaderNavView uses to populate itself.
import pystache
import fnmatch
import os
import re
import datetime

def mod_date(filename):
    t = os.path.getmtime(filename)
    return datetime.date.fromtimestamp(t).strftime("%B %d, %Y")
def last_mod(filename):
    t = os.path.getmtime(filename)
    return datetime.date.fromtimestamp(t).strftime("%Y-%m-%d")
def creation_date(filename):
    t = os.path.getctime(filename)
    return datetime.date.fromtimestamp(t).strftime("%B %d, %Y")
def skillsReplacement(matchobj):
    # group(0) is entire match, group(1) is content
    return '<p class="skills">Skills used: '+matchobj.group(1)+'</p>'

 
rootPath = '.'
pattern = '*.html'
xmlString = """<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
"""
urlTemplate = """    <url>
        <loc>http://www.craftedplaces.com/{{route}}</loc>
        <lastmod>{{modDate}}</lastmod>
        <label>{{label}}</label>
    </url>
"""
section = ''
sectionUrl = ''
otherUrls = ''
sections = {}
for root, dirs, files in os.walk(rootPath):
    for filename in fnmatch.filter(files, pattern):
        # Open input file
        print(os.path.join(root, filename))
        print(mod_date(os.path.join(root, filename)))
        inputFile = open(os.path.join(root, filename), errors='ignore')
        inputString = inputFile.read()
        inputFile.close()

        print(section + "-" + root[2:])
        # add XML data to sitemap string
        if root == '.':
            # home page, unique in having shorter route
            route = ''
            matchObj = re.search('<navlabel>(.*)</navlabel>',inputString)
            label = filename.split('.')[0] if matchObj is None else matchObj.group(1)
            xmlString += (pystache.render(urlTemplate, {'route':route,'modDate':last_mod(os.path.join(root, filename)),'label':label}))

        else:
            if section != root[2:]:
                # new section, add strings to buffers and reset vars
                if section != '':
                    sections[section] = sectionUrl + otherUrls
                section = root[2:]
                sectionUrl = otherUrls = ''

            route = '#' + section
            matchObj = re.search('<navlabel>(.*)</navlabel>',inputString)
            label = filename.split('.')[0] if matchObj is None else matchObj.group(1)
            if section == filename.split('.')[0]:
                sectionUrl += (pystache.render(urlTemplate, {'route':route,'modDate':last_mod(os.path.join(root, filename)),'label':label}))
            else:
                route += '/' + filename.split('.')[0]
                otherUrls += (pystache.render(urlTemplate, {'route':route,'modDate':last_mod(os.path.join(root, filename)),'label':label}))
        
        # Process input string
        inputString = re.sub('src="../','src="',inputString)
        inputString = re.sub('href="../','href="',inputString)
        inputString = re.sub('<navlabel>.*</navlabel>
','',inputString)
        inputString = re.sub('<p class="skills">(.*)</p>',skillsReplacement,inputString)
        inputString = re.sub('<p class="mod-date">.*</p>','<p class="mod-date">This page last modified: '+mod_date(os.path.join(root, filename))+'</p>',inputString)

        # Open/create output file
        outputPath = re.sub('^\.','..\html',root)
        try:
            outputFile = open(os.path.join(outputPath, filename), encoding='utf-8',mode='r')
            currentString = outputFile.read()
            outputFile.close()
            if inputString == currentString:
                print('Same, continuing...
')
                continue
        except IOError:
            print("Couldn't open " + os.path.join(outputPath, filename) + "\n")

        outputFile = open(os.path.join(outputPath, filename), encoding='utf-8',mode='w')
        print("Overwriting/creating " + os.path.join(outputPath, filename) + "\n")
        outputFile.write(inputString)
        outputFile.close()

# add last section
sections[section] = sectionUrl + otherUrls
#xmlString += sectionUrl + otherUrls

# manually control section order
xmlString += sections['about']
xmlString += sections['projects']
##### manually handle blog section
xmlString += """    <url>
        <loc>http://www.craftedplaces.com/wp</loc>
        <label>blog</label>
    </url>
"""
xmlString += sections['contact']
xmlString += "</urlset>"

sitemap = open('../sitemapPlus.xml', encoding='utf-8',mode='w')
sitemap.write(xmlString)
sitemap.close()

# strip <label> tags which Google doesn't like
xmlString = re.sub('\s+<label>.*</label>','',xmlString)

sitemap = open('../sitemap.xml', encoding='utf-8',mode='w')
sitemap.write(xmlString)
sitemap.close()
#input("Press Enter to terminate.")

encodeVideo.bat

I have to admit, I hate trying to write Windows batch files. Doing even the simplest things with them is counter-intuitive and needlessly difficult to me, but I do use them from time to time. In this case, I wanted to automate the process of encoding and copying all of my video files.

There are free GUI video-encoding tools which do what I want, but they don't support batch processing in any sort of robust way and the thought of manually processing the many dozens of videos on my site was onerous. So I created a batch file to copy over directory structure from my source folder and process all the videos with ffmpeg, a very powerful command-line tool. I use this with differing compression levels to encode and resize the videos on my site, and I even create the HTML5 <video> posters with it by taking a screenshot of the first frame of each video!

processImages.bat

Likewise, I created a batch file to process images, resizing them as needed and applying a certain level of compression. Thumbnails are created at the same time.

 

Skills used: HTML5, Graphic Design, Responsive CSS, JavaScript

This page last modified: June 07, 2019