js: angular touch delay/lag on mobile

I had built InviteAgain.com to seamlessly work on mobile devices. This was the first project in Angular where I wasn’t handling my own mouse/touch up and down, and I just planned to rely on ng-click, and default mouse/touch behaviors to handle the table scrolling.

I was getting what felt like bad lag when running the app on mobile. The connection to the database would load and refresh data immediately, but the user interaction, ie touch, was taking forever to respond. So after some head bashing, I managed to learn the hard way that the library angular-touch, made by angular, is pretty much a requirement for mobile if you are going to rely on ng-click, and not handle your own mouse up and down.

Angular-touch modifies the standard ng-click to remove a 300ms delay mobile browsers otherwise impose while waiting to see if the user is going to double tap. Problem solved!

js: parsing csv files in angular with papa parse

I’ve been trying to embrace/balance using more libraries and less native code. The CSV parsing I needed to do on InviteAgain.com was a great candidate. I found PapaParse. Perfect. Simple javascript class that can handle all use cases via a single settings object.

I setup an Angular directive and template that I can plug in anywhere to get a custom file input UI. That UI contains another directive linked to the file input event. On file input angular hands the csv off to the Papa object. I tell it the data has headers, to use web workers, and to run $apply() on the scope after it’s completed parsing the file. Papa hands me back a javascript object with all the csv’s key/value pairs. Done.

.directive('onReadFile', function ($parse) {
  return {
    restrict: 'A',
    scope: {conSec: '=', current: '='},
    link: function(scope, element, attrs) {
      element.on('change', function(onChangeEvent) {

      var file = (onChangeEvent.srcElement || onChangeEvent.target).files[0];

       Papa.parse(file, {
          header: true,
          worker: true,

          complete: function(results) {
            if(scope.conSec.name){
              scope.conSec.fileInput.err = results.err;
              scope.conSec.fileInput.data = results.data;
              scope.conSec.fileInput.meta = results.meta;
              // Need to force the update
              scope.$apply();
            }
          }
        });
      });
    }
  };
})

.directive('fileInput', function(){
  return {
    restrict: 'E',
    scope: {conSec: '=', current: '='},
    templateUrl: "../templates/fileInput.html"
  };
});

devops: mysql server on azure ubuntu vm

Full disclosure, there are a lot of tutorials for this on the azure website. I just found that every one of them had either enough errors, or enough missing pieces, that it made it impossible to get setup correctly unless you knew what you were doing, so hacked quite a bit of them together.

Go to manage.windowsazure.com
Click Virtual Machines > New > From Gallery
(I’m using Ubuntu server 14.04 LTS)

Virtual Machine Name: yourvm
(This will become the address in yourvm.cloudapp.net)

New User Name: yourvmusername
(this will be used anytime you need to access the vm)

UNCHECK: UPLOAD COMPATIBLE SSH KEY FOR AUTHENTICATION
(you are welcome to handle keys yourself, I’m just going for passwords)

CHECK: PROVIDE A PASSWORD: yourvmPassword!
(this will be the password to access the vm)

SET: create new cloud service

CHECK: VM agent

COMPLETE

Open Terminal. Connect Via SSH:
ssh yourvmusername@yourvm.cloudapp.net -o ServerAliveInterval=180
//You will see the following, enter yes, then your vm password:
The authenticity of host 'yourvm.cloudapp.net (255.255.255.255)' can't be established.
Are you sure you want to continue connecting (yes/no)? yes

Warning: Permanently added 'yourvm.cloudapp.net,255.255.255.255' (RSA) to the list of known hosts.
yourvmusername@yourvm.cloudapp.net's password:
yourvmPassword!

Welcome to Ubuntu 14.04.2 LTS (GNU/Linux 3.16.0-43-generic x86_64)
//a bunch of text
//then you'll be at the prompt: yourvmusername@yourvm:~$

Update the VM:
sudo apt-get update
(could take a couple of minutes, bunch of text)
//back to prompt

Install MySQL Server on the VM:
sudo apt-get install mysql-server
//bunch of text
Do you want to continue? [Y/n] Y

UI popup for password for root user on mysql server: yourSQLpw
(this will be how you initially access the mysql server, via un: root, pw: yourSQLpw)
//Confirm it, then back to terminal, lots of text spam, even some things that look like errors or warnings “sent invalidate” and “using unique option prefix” just move on.
//back to prompt

Install MySQL Client on the VM:
sudo apt-get install mysql-client
//bunch of text
//back to prompt

Test if the Service is Running:
sudo service mysql status
//should see: mysql start/running, process etc

Now Open Port 3306 on the VM:
sudo iptables -A INPUT -i eth0 -p tcp -m tcp --dport 3306 -j ACCEPT

Test that the Port is Open:
sudo netstat -anltp|grep :3306
//you should see something like:
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 3259/mysqld
(3306 in red, that doesn’t mean bad, just that it found that number)

SSH Tunnel from 3307 to 3306:
(Now we create a tunnel on the VM that takes public connections from 3307 and locally sends them to 3306, which is the mysql server)
sudo ssh -fNg -L 3307:127.0.0.1:3306 yourvmusername@yourvm.cloudapp.net
//warnings about authenticity will pop up:
yes
//then you’ll see yourvmusername@yourvm.cloudapp.net’s password:
(enter your vm password, NOT your mysql password)
yourvmPassword!
//back to prompt

Go Back to manage.windowsazure.com

Setup End Point on the VM:
(this will be the public port 3307 that we add to the VM, which will then hit the SSH Tunnel we made, and route all traffic to the mySql server)
Click on yourvm in the VM tab
Click Endpoints
Click ADD+ button at the bottom of the screen
Name it anything, I named mine 'MySQL Tunnel'
Use TCP
Public Port 3307
Private Port 3307
Don't check any boxes

DONE WITH SETUP!

Now to Connect to the database from Node or PHP, etc, You Use:
URL: yourvm.cloudapp.net
Port: 3307
User: root
Password: yourSQLpw

(obviously you can create other user accounts and not use root if you want the edit trail)

To Connect to the VM from CLI:
ssh yourvmusername@yourvm.cloudapp.net -o ServerAliveInterval=180
password: yourvmPassword!

Access MySQL Directly from the VM CLI:
mysql -u root -p
password: yourSQLpw

git: history reclaim/redo/fix

The standard instructions confused me, so here they are simplified with better references to account and repo specific variables.  (taken almost entirely from github instructions)

–Open a terminal

//write down the old email shown in git log
git log

 

–CD into your current repo

//the following will create a temp sub folder containing a packed version of the repo
git clone --bare https://github.com/YOURUSERNAME/THISREPONAME.git
//now cd into the new temp directory
cd THISREPONAME.git

 

–Create this bash script:

#!/bin/sh
#replace the 3 lines for old, correct name , correct email
git filter-branch --env-filter '

OLD_EMAIL="your-old-email@example.com"
CORRECT_NAME="Your Correct Name"
CORRECT_EMAIL="your-correct-email@example.com"

if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_COMMITTER_NAME="$CORRECT_NAME"
export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_AUTHOR_NAME="$CORRECT_NAME"
export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags

 

–Execute the bash script in the new temp folder

–Replace entire remote with entire local

git push --force --tags origin 'refs/heads/*'

 

–Delete the temp folder

cd .. //(into main folder)
rm -rf THISREPONAME.git

js: hhgholders.js, a 2d object oriented sprite based engine v0.5.1

hhgholders.com

This is a 2d object oriented engine, written in javascript. It only uses the DOM for rendering, while extrapolating everything else into a standard sprite system, akin to Cocos2d or Apple’s SpriteKit, based on sprites and actions.

The standard usage is to simply use the HHgHolder class and then allow the engine to handle the rest. You get to completely ignore HTML and CSS. But all elements remain as standard DIVs behind the scenes, so any modifications can be made to HTML/CSS/Javascript that would be made to a standard web page.

The engine functions entirely independantly of the DOM, except for HHgScene.js which updates all the dirty visible elements per frame. HHgScene could be modified to “translate” the engine’s data into any one of a number of visual forms/renderers. The rest of engine is agnostic.

The engine is most notably missing a physics system, but in general is not really designed to be a thousands of things bouncing and updating engine. Though one could easily be added and then use updates on HHgHolders.


Usage Doc v0.1.5
(This is much more easily read on github, or in the local repo)
//=============================================================
Preface: for the most part, you should not need to modify many engine files
          except for:
          HHgConfig.js (top level and settings)
          HHgGame.js (the entry point for custom code)
          HHgTests.js (test and debug settings)

1. The scene will automatically scale visually to the width of the screen.
  a. But all measurements are done on a point system of 1920/1080.
    aa. so even if you are on a tiny phone, or in a small browser window, you would still commonly use 1920 as screen width (if set to landscape).
  b. All of these settings can be changed in HHgConfig.js

2. HHgLoadingScreen.js is loaded before any engine processing takes place.
  a. This is the only other file you would need to modify.

3. HHgGame.doStart() is called by the scene once setup is finished.
  a. Modifying HHgTestsOverride in HHgTests.js will use the doStart() in HHgTests.js as a starting point.
  a. Otherwise the engine will run the default demo doStart() located in HHgGame.js.

4. HHgActionManager runs a requestAnimationFrame loop which has 3 steps per frame:
  a. calculate outcomes of all HHgActions.
  b. calculate final results of these on all HHgHolders.
  c. tell scene to update all visual elements.
  d. NOTE: it also calls HHgGame.doLoop once per frame, but making use of it is not required

5. HHgHolder is the only visual element in the engine.
  a. It holds images/sprites, color, text/paragraph text, or other HHgHolders:
    var someHolder = new HHgHolder({w: 100, h: 100});
    aa. but unless you specifally choose not to use it, you should use the ObjectPool to get holders:
      var someHolder = var HHgGetHolder({w: 100, h: 100});
      aaa. and then return the holder to the pool when no longer needed:
        HHgReleaseHolder(someHoldervar);
    ab. a holder needs to be added to a parent holder, HHgGameHolder is created by default, in order to be visible, and then needs a sprite/image, color, or text.
      someHolder.doMoveToNewParent({parent: someOtherHolder, position: new HHgVector2(-200,-200), isScreenPos: true});
      someHolder.doAddSprite("testSprite");
      someHolder.doAddParagraphText({text: "my text", color: {H: 255, S: .5, L: .5, A: 1}, shadow: {color: "black", x: 4, y: 4, blur: 4}});
      someHolder.setMouseable(true);
      someHolder.setIsDraggable(true);
  b. Please note that many of the properties (most) require the use of getters/setters
    ba. This is to allow for specific calculations and scene updates when values are changed.
      var someNumber = someHolder.getWidth();
      someHolder.setPositionInScreenTo(29,45);
      someHolder.setPositionInParentTo(new HHgVector2(200,200));
  c. HHgHolders are automatically updated when their parent holders are updated by Position/Rotation/Scale.
  d. holders can be positioned in a parent, or in screen. and each one updates the other.
    da. So if you call someHolder.setPositionInScreenTo(0,0), its position in parent will change.
      daa. var newVector = someHolder.getPositionInParent();
  e. HHgHolders depth is based on their zIndex property. Changing the zIndex of a parent will adjust all children

6. HHgGameHolder is the top level Holder in the hierarchy.
  a. any HHgHolders you wish to add to the game and not be parented to another Holder should be parented to this:
    someHolder.doMoveToNewParent({parent: HHgGameHolder, position: {x: 200, y: 200} });
    aa. providing no parent argument will default to adding to HHgGameHolder.
  b. HHgScene is technically an HHgHolder, but scale and position will be incorrect for children.

7. Properties Object - most classes and high level functions expect a javascript {} to be passed as arguments.
  a. The HHgHelper object then runs one function per expected argument to process and filter the input
    aa. This means that argument passing tends to be flexible, so for size you could pass in:
      aaa. {size: 100} or {w: 100, h: 100} or {size:{w: 100, h: 100}} etc.
  b. But for complex classes like creating an action, you'll pass in a single object with multiple key value pairs.
  c. Almost all values are optional, and will be filled in with a sensible value if omitted, like 0,0 for position.

8. HHgAction is what holders use to animate or change or set timers.
  a. You do not need to create HHgActions directly, you can use helper functions of .doAction("Move") or:
  aa. helper functions on HHgHolder
    someHolder.doActionRotateBy({rotation: HHg.returnRandomInt(120,720), time: HHg.returnRandomInt(5,35)});
    someHolder.doActionFollowQuad({cx: 0, cy: 540, x: 960, y: -540, time: 10, easeIn: 25, easeOut: 25 });
  b. The holder and the actionManager will handle the rest.
  c. You can pass a 'name' key/value into the above functions to uniquely name an action on an object.
    ca. This then allows you to call doRemoveActionByName(name);
  d. passing a key for 'onComplete: someFunction' to the function will cause the passed function to be executed on completion.
  e. HHgHolder has actions for:
    doActionRotateBy(any amount from negative to positive, time);
    doActionRotateLeftTo(any degree from 0 - 360, time); doActionRotateRightTo(); doActionRotateForever(+/- rotation per second);
    doActionMoveInScreenTo(x,y, time); doActionMoveInScreenBy(); doActionMoveForever(vector per second);
    doActionScaleTo(positive value, positive value, time); doActionScaleBy(positive value, positive value); doActionScaleForever(scale per second);
    doActionFollowQuad(vector control, vector destination, time); //creates a quadratic path using the current xy, a control xy, and final xy
   f. //TODO make this more complete: but there are also Sequences and Clusters of actions, and helper functions to create and save actions, which can then be added to sequence, or run forever, etc.

9. Timers - do not use setTimeOut or setInterval anywhere, as this will cause things to fall out of sync
  9a. Instead call a timer action on an HHgHolder
    someHolder.doActionTimer({time: 10, onComplete: someFunc});

10. Values
  a. the xy grid for positioning on the screen, and in Holders is 0,0 centric with Y-up x,y coordinates (default point dimension is 1920/1080)
  b. time is always in seconds
  c. colors are always eventually in an HHgColorRGBA object, but most classes and functions will take HSL, RGB, or Hex values
    ca. this just means that passing in arguments other than a color object will result in a conversion step.
  d. all position/rotation/scale values are in points, so 1 or 1.5 or .044567. no strings, no trailing "px", etc.
    da. the engine will handle all of the conversions in the HHgScene.js file.

11. Mouse and Touch
  a. there are built in mouse and touch and drag handlers on HHgMouse.js, the engine will use the correct ones based on device type.
  b. you should not need to modify HHgMouse.js, instead, override the existing HHgHolder methods:
    doMouseDown(); doMouseUp(); doStartMouseMove(); doMouseMove(); doEndMouseMove();
  c. the engine calculates a holder as 'touched' based on z-index and sprite pixel alpha;
    -note: if a holder is set to mouseable, but has no sprite, then the holder width and height will be used.
    ca. but a Holder must have mouseable set to true, which is set to false by default.
      someHolder.setMouseable(true) to receive any mouse or touch interactions.
  d. for a holder to recieve the move events, it must be set to draggable.
    da. someHolder.setIsDraggable(true);
  e. HHgMouse recieves an array of all qualifying Holders for mouse/touch, it only uses the highest element in the array.
    ea. if you need to access a "stack of holders" for something like dragging a stack, then you'll need to modify HHgMouse.js.

12. HHgColor
  a. all color in the engine eventually comes down to R: 1-255 G: 1-255 B: 1-255 A: 0-1
    aa. and creating a new color object:
      var someColor = new HHgColorRGBA(255,255,255,1);
    ab. there is also an HSL color object: H: 0-360, S: 0-100, L: 0-100, A: 0-1
      var someColor = new HHgColorHSLA(360,100,100,1);
    ac. HHgColorHelper has methods to convert to and from RGB, HSL and Hex
      aca. many functions and classes will do this conversion automatically if you pass in anything other than RGBA
  b. HHgColor objects also have methods like .lighten(color, percent).
  c. HHgColorHelper has methods like .blendRGBA(color1, color2, percent).

13. HHgVector2 //TODO update vector names and functions
  a. HHgVector2 is the basis for passing grid coordinates, vectors, and scales.
    var someVector = new HHgVector2(50.5,323.7);
  b. HHgVector2's contain all functions for vector math: add, subtract, divide, etc.
    ba. these are also all versioned with aliased and verbose names:
    bb. they also return a new vector, and do not modify the existing vector
      var someVector = vector1.returnVectorPlusVector(vector2);
      var someVector = vector1.returnAdd(vector2);
      var someVector = vector1.returnVectorScaledBy(vector2);
      var someVector = vector1.returnMultiply(vector2);
  c. to immediately modify existing vectors, use the 'plusEquals'/etc functions.
    someVector.plusEquals(someOtherVector); someVector.addEquals(someOtherVector);
    someVector.divideEquals(someOtherVector);
  d. Other useful functions:
    someVector.returnDistanceToVector(someOtherVector);
    someVector.returnVectorRotatedAroundVectorAtAngle(someOtherVector, angle); //this will all be handled for you in actions and they rarely ever need to used directly
    someVector.returnVectorAtDistanceToVector(someOtherVector);
  e. to save memory allocation you can use someVector.setXY(x,y) to change the values of a vector.
    ea. but be very careful using this, as vectors tend to get passed around, and these are objects passed by reference:
      eaa. unlike in many languages where a vector would be a struct passed by value.

14. HHgText
  a. all text is displayed in engine by adding it to HHgHolders.
    aa. this text is one of 2 types:
      aaa. someHolder.doAddParagraphText({text: "my text", color: {H: 255, S: .5, L: .5, A: 1}, shadow: {color: "black", x: 4, y: 4, blur: 4}});
      aab. someHolder.doAddCanvasText({text: "my text", color: {H: 255, S: .5, L: .5, A: 1}, shadow: {color: "black", x: 4, y: 4, blur: 4}});
    ab. paragraph text creates a 'pre' (preformatted text) element inside of the div.
      aba. this text is then modified via standard CSS.
      abb. when the div is scaled, the scene recalculates the font size to adjust for the div scale.
      abc. this text is not part of the canvas, and is therefore not subject to mouse/touch tests.
    ac. canvas text is created by formatting a 2d context and then painting the text directly onto the div's canvas
      aca. there are different pros and cons for rendered/rasterized text. Canvas text will also be part of the alpha pixel test for mouse interactions.

15. Adding and Modifying
  a. the engine is designed to have anything and everything modded, but I recommend not changing anything on HHgHolder or HHgScene:
    aa. unless you understand exactly what it's doing.
  b. tack any properties onto holders that make sense to you, and create any custom controllers to handle game logic.
  c. HHgActionManager runs a set of functions every frame that receive the delta time since the last frame:
    ca. actions already handle all of their updates via this delta
    cb. but you can also make use of HHgGame.doLoop() which is called each frame if you were going to add somthing like a physics system, etc.
  d. you can also use the doAddCSSClass() and doRemoveCSSClass() to use custom CSS class functionality in holders.
    da. but I don't really recommend it, as things like actions and canvases won't always play nice with them. But they can be handy if you are using the engine for making more static things, or just for pure styling.
  e. the engine uses the file HHgMain.css as it's only css file.
    ea. this file resets browser behavior, adds clearfix, and sets up some basic table functionality for text in divs
    eb. you are welcome to add, but be careful of modifying any of the existing css elements, as alot depends on them.

15. Usage and Quirks
  a. you'll notice that pretty much any function that you are supposed to interact with starts with "do" to do something or "return" to calculate and return something
  b. or "get" and "set" for property access. the engine does a lot of secondary computation after properties are set.
    ba. so this requires the use of functions instead of standard .access
  c. most functions require property objects to be passed as arguments {R: 255, G: 255, B: 255};
    ca. but if a function just takes 1 concept, then it usally can be short-handed for faster typing:
      funcTakesColor(255,255,255); or funcTakesColor(new HHgColorRGBA({R:255, G:255, B:255, A:1}));

node: mysql column name collisions on select, join

I’ve been playing around with the MySQL Node module. I came to a place where the async javascript callback result for selects on multi-table joins started having key name collisions.

In comes nestTables:true. Wrapping the select query in an object with the nestTables property set to true will cause the result object to use nested objects for each table.

//Return Obj has name collisions
  db.query('SELECT parents.name,\
                   jobs.type, \
                   locations.name \       
            FROM parents JOIN jobs \
              ON jobs.id=parents.jobId \
                         JOIN locations \
              ON locations.id=parents.locationId',
            function(err, result){
              console.log("LOG:",result);
              //LOG: [{type:'Teacher',
              //       name:'Home'}]
          });
//Return Obj is now broken out by table with sub properties
  db.query({sql: \
           'SELECT parents.name,\
                   jobs.type,\
                   locations.name\
            FROM parents JOIN jobs \
              ON jobs.id=parents.jobId \
                         JOIN locations \
              ON locations.id=parents.locationId',
            nestTables:true},
            function(err, result){
              console.log("LOG:",result);
              //LOG: [{parents:{name: "Joe"},
              //       jobs: {type:'Teacher'},
              //       locations:{name:'Home'}}]
          });

Note: you can also use nestedTable: "_" to force the result to use TableName_ColumnName as the object keys.

meteor.js: easier default account system files

The account backend in meteor is pretty decent, and is wired up to all the basics, but manipulating the front end is an exercise in frustration, and wiring up the additional features is a headache. The CSS is all wrapped up in compiled files and LASS. So you install one of the packages that removes the styling, but even that makes it a huge pain to change or manipulate the UI. And even then, that UI is tied to some of the basic vanilla behavior and leaves out a lot of the smart and complex features that are available and sometimes necessary.

So instead of making a package that would just be difficult to customize to my needs on projects going forward, I baked all features of the Meteor account system down into 3 files:

customAccounts.html
customAccounts.css
customAccounts.js

By default, all the features are in place, and the UI is styled to match meteor’s home page. As well as being broken into easy to understand templates for all states and use cases…using actual buttons instead of a weird hybrid of hyperlinks and buttons. Every account related feature and function is either wired up in the javascript file, or if it’s a less common feature, then stubbed in with example uses.

Just drop ’em into your project folder, no package installation required.
https://github.com/CHBDev/Complete-Meteor-Account-Pipeline

bash: create HTML with automatic -scripts- and -links-

I was frequently writing small projects, or test projects, and found myself just needing an index.html hull with a number of links to various local js files and css files, as well as cdn based libraries.

Here’s a bash command I wrote that will create a local index.html file and search through all of the arguments provided to the command to automatically insert js or css files by name, or cdn libraries by partial match. Obviously you could add any number of match criteria to the script. Note: you will need to manually update the cdn addresses for library version numbers as time goes on.

By default it partial matches for backbone, underscore, jquery, d3, and the usual suspects.

Usage:> makehtml anyjsfile.js anycsfile.css underscore backbone d3 partialmatchkey partialmatchkey…etc

#!/bin/bash

FILE="index.html"

touch $FILE
echo "<!DOCTYPE html>" > $FILE
echo "<HTML lang="en">" >> $FILE
echo "<HEAD>" >> $FILE
echo "<meta charset="UTF-8">" >> $FILE

function addCSS() {
  str=$1;
  echo "<LINK rel=\"stylesheet\" href=\"$str\">" >> $FILE
};
function addJS() {
  str=$1;
  echo "<SCRIPT src=\"$str\"></SCRIPT>" >> $FILE
};

lowerString=1

function toLower()
{
   local __word=$1
   local __len=${#__word}
   local __char
   local __octal
   local __decimal
   local __result

   for (( i=0; i<__len; i++ ))
   do
      __char=${__word:$i:1}
      case "$__char" in
         [A-Z] )
            printf -v __decimal '%d' "'$__char"
            printf -v __octal '%03o' $(( $__decimal ^ 0x20 ))
            printf -v __char \\$__octal
            ;;
      esac
      __result+="$__char"
   done
   lowerString="$__result"
};

for thing in $@; do
  if [ $FILE = $thing ]; then
    continue;
  fi

  toLower $thing

  if [[ $lowerString == *".css"* ]]; then
    echo "add css"
    addCSS $lowerString
    continue
  fi
  if [[ $lowerString == *".js"* ]]; then
    echo "add js"
    addJS $lowerString
    continue
  fi
  if [[ $lowerString == *"bootstrap"* ]]; then
    addCSS "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"
    continue
  fi
  if [[ $lowerString == *"jquery"* ]]; then
    addJS "https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"
    continue
  fi
  if [[ $lowerString == *"angular"* ]]; then
    addJS "https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"
    continue
  fi
  if [[ $lowerString == *"three"* ]]; then
    addJS "https://ajax.googleapis.com/ajax/libs/threejs/r69/three.min.js"
    continue
  fi
  if [[ $lowerString == *"webfont"* ]]; then
    addJS "https://ajax.googleapis.com/ajax/libs/webfont/1.5.10/webfont.js"
    continue
  fi
  if [[ $lowerString == *"backbone"* ]]; then
    addJS "https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min.js"
    continue
  fi
  if [[ $lowerString == *"underscore"* ]]; then
    addJS "https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.2/underscore-min.js"
    continue
  fi
  if [[ $lowerString == *"d3"* ]]; then
    addJS "https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"
    continue
  fi

done

echo "</HEAD>" >> $FILE
echo "<BODY>" >> $FILE
echo "<p>===============<p>" >> $FILE
echo "<p>CONTENT OF PAGE<p>" >> $FILE
echo "<p>===============<p>" >> $FILE
echo "</BODY>" >> $FILE
echo "</HTML>" >> $FILE

js: the requestAnimationFrame() game loop

Historically, web/js based apps had to basically set their own “loop” using setInterval or setTimeout. There’s actually not a huge downside to using those, except when framerate becomes an issue. So now we can use requestAnimationFrame(). It has a number of uses, but the simplest is just to have it handle the entire game loop.

Essentially RAF calls a callback function every time the interpreter can come up for air. This combined with tracking the delta in time between frames can allow you to handle changes in framerate more smoothly.

In the implementation below calling myGame.actionLoop() calls recurse(), which then has requestAnimationFrame call the recurse loop repeatedly. I’ve broken the specific game logic out into the function var doThisEveryFrame just to clearly illustrate the effect.

myGame.actionLoop = function() {
    var lastFrameTime;
    var deltaT;
    var lowEnd = 1;
    var highEnd = 160;

    var doThisEveryFrame = function(timeDiff){
      myGame.updatePhysics(timeDiff);
      myGame.updateLogic(timeDiff);
      myGame.renderChangesToScreen();
    };

    function recurse( thisFrameTime ) {
        window.requestAnimationFrame(recurse);
        thisFrameTime = thisFrameTime && thisFrameTime > 5000 ? thisFrameTime : window.performance.now();
        lastFrameTime = lastFrameTime || thisFrameTime;
        deltaT = thisFrameTime - lastFrameTime;

        if (deltaT >  highEnd){
          deltaT = highEnd;
        }
        if(deltaT > lowEnd){
          doThisEveryFrame(deltaT);
          lastFrameTime = thisFrameTime;
        }
    }
  recurse();
};

mysql: early testing & foreign key syntax

Early on in a local project, I kept finding out of date, or strange references to setting up Foreign Keys in various complicated situations in MySQL, this resulted in not only me being confused, but in constantly needing to drop tables and rebuild them. Which was annoying.

Using Node as the backend framework, it dawned on me, that I could just have three terminals open, one running the Node server, one using the mysql> prompt, and the third running terminal commands to rebuild the database using the schema.sql file. Failing quickly is always better than failing slowly.

So the order went:
1. Test something, screw up database.
2. Terminal 1: mysql> drop database people;
3. Terminal 2: $: mysql -u root < schema.sql
4. Terminal 3: execute stuff on server
5. Go back to 1.

Here's an example schema file, in case you came here just looking for Foreign Key syntax:

CREATE DATABASE people;
USE people;

CREATE TABLE parents(
  id INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(20) NULL DEFAULT NULL,
  PRIMARY KEY(id)
);

CREATE TABLE locations(
  id INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(20) NULL DEFAULT NULL,
  PRIMARY KEY(id)
);

CREATE TABLE children(
  id INT NOT NULL AUTO_INCREMENT,
  name VARCHAR(140) NULL DEFAULT NULL,
  parentId INT,
  locationId INT,
  PRIMARY KEY(id),
  INDEX(parentId, locationId),
  FOREIGN KEY (parentId) REFERENCES parents(id) ON DELETE CASCADE,
  FOREIGN KEY (locationId) REFERENCES locations(id) ON DELETE CASCADE
);