Wednesday, May 27, 2015

5/26/15 and 5/27/15 - MapMe (with OpenStreetMap and Leaflet)

I've been working on creating an app that loads a user's location using the HTML5 location API, then draws that location on a slippy map (A map you can drag around with your cursor, such as Google Maps). The app is powered by OpenStreetMap data, and uses a library called Leaflet to generate the slippy map. The tiles are loaded from a webserver that hosts OpenStreetMap data without needing an API key, which is good for our tutorial.

I actually ended up with two versions of this app—one has an additional Leaflet UI button to regenerate the user location. For the purposes of the tutorial, we decided that this was a bit beyond the scope of what we were trying to teach. The version that will be included in the tutorial is here, while the more advanced version is here.

Here's some screenshots of the version that will be in the tutorial.  Users can click the 'find my location' button as many times as they want!


The initial screen

Once the user's location is found

The map can be zoomed and moved

Here's my HTML for the button and map, and here's my CSS.
In order to get the map to fill the area of the screen not taken up by the location button header, I put both the map and the button inside a div with display set to table.  I set the display of the map container, .content, to a table-row, which makes it take up the remaining width on the page.  I initially had the .button-header class also set to table-row, but with that set, the element padding wasn't being applied, so I removed it.

5/26/15 - Neat Closure Scope

I did something cool today!  In NotesDemo, I wanted to move this click event out of $(document).ready into an object, but the click event needs access to the notes_make object.  After reading JavaScript: The Good Parts, I knew that I could create a function that returns another function, which I did here.  This puts the object passed into the function as notes_make within the scope of the returned function!  Then I can do this, as notes_make is accessible inside the returned function!

Thursday, May 21, 2015

5/21/15 - UI Encapsulation Object?

I'm still in the process of structuring the various objects in NotesDemo so that they can know the least about each other and still function. Today, I split the ui handling object into two, with one object handling the interface for inputting notes, and the other handling the display of the notes on screen.

I moved the trash button click function into the latter object, because I realized that with my previous code, notes created after document.ready() was evaluated wouldn't have the event.  This forced me to pass in my notes storage manager as an argument, since I need the trash function to get the parsed index from the note element id and delete the note.  Before this, I had managed to keep all the objects completely separate from each other.

I also ended up creating an object which encapsulates all the UI behavior coded so far, a ui manager.  I'm not too comfortable with this style of programming, so I don't know if I'm missing a better way to go about this.  It's essentialy just a hash with two keys mapped to each specific UI object.  Then, a user of the object can do ui.note_input and ui.note_disp to get the two specific objects. It might not even be worth making a full constructor function for this, and instead just setting it up in the document.ready function.

I found it interesting that this line works without me having to encapsulate trash_click_fn in an anonymous function as would be necessary if creating an object.

I'm still not sure why javascript requires encapsulation, for example,
function encapsulating_function () {
  var pre_defined_function = ...
  return {
    bad_func:    pre_defined_function,
    good_func:   function () { pre_defined_function },
    better_func: function () { pre_defined_function.apply(this, arguments) }
  }
}
where bad_func won't work, good_func will work but only if the function expects no arguments, and better_func will work in all cases pre_defined_function would work).

Actually, what I just wrote is wrong. I just tested it, and #bad_func and #better_func give the user access to pre_defined_function, while good_func returns undefined. I'm so confused :S

I also did a minor css change and added the property -moz-user-select: none; to the header class. This nicely applies to all sub-elements within the header, and so it prevents users from highlighting the app title 'Notes' or the '-' on the input toggle button by long-pressing.

Wednesday, May 20, 2015

5/20/15 - Peasant Multiplication

Today in math, we learned about a method of multiplication called Peasant Multiplication that works for positive whole numbers, and is pretty cool. Here's a rundown on how it works, although it's kind of hard to understand and I'm not sure how well the website explains it.

I implemented this in ruby!
View it in action here by pressing the run button

The full code I wrote is here, which is extensible in case I get around to testing the efficiency of this algorithm compared to other methods.

Tuesday, May 19, 2015

5/19/15 - Local Storage with Hash

I opted to use a hash in the local storage over an array, because that gives more flexibility to the indexing of the notes.  For example, if a user has notes with id's note_1, note_2, note_3 and he decides to delete notes 1 and 2, the first two key/value pairs can be removed without affecting the data in note_3. In my current implementation, with a hash that looks like {3: Note object}, a new note would be added at index 4, even though 1 and 2 are unfilled. I don't think that this is too big of an issue, as it's not like that really puts an upper limit on the amount of notes a user could have.

If I really didn't like the idea of the user getting up to notes with indexes in the hundreds or thousands after months of use and deletions, I could have the indexes be recalculated each time the app is run (so if the app loads a hash from localstorage with arbitrary keys {10: ... 50: ... 51: ...}, it would reposition the hash in memory so that the keys would be {1: ... 2: ... 3: ...}.

Since the hash is only ever read from localstorage once, when the app first opened, this would actually be pretty trivial to do, I would just have to add a bit more code here so that after the object was parsed, the keys were repositioned. I'm not sure if this is something worth doing for the presentation, though.

I'm still not satisfied with how my app objects are structured, but I don't have enough javascript experience to know what I can do to fix it. I can't even really describe what I'm unhappy about, other than the imbalance between the number of functions in each object, which I mentioned yesterday as well.

One specific thing I don't like is that in order for the notes to be persistent, they are loaded into the ui from the local storage here, in the $.ready event. However, this forced me to make the notes_hash object public, which would allow a user of this object to bypass my interface for manipulating the hash. I can do some jQuery to clone the object, but I don't know if there's a significant performance cost associated with that, and so far the security of the hash doesn't really matter so I just left it as is. At the same time, a performance cost wouldn't really matter, because the method is only being called once. I guess it's just a matter of personal preference.

Monday, May 18, 2015

5/18/15 - Local Storage and Refactoring

Today I worked on refactoring the object structure of my app. I'm still not happy with it, and here's a list of some problems I see:
  • A lot of behavior is being defined directly in the .ready() event, which probably could be moved to an object.
  • The object sizes are really unbalanced: notes_make_manager only has one function defined, while the other two have 5+
  • A lot of variables are being evaluated by jQuery rather than being passed in as arguments, so there are often non-obvious dependencies on the html for the code to work properly. An example is this code, which is getting the user inputted note from the boxes. It might make more sense to use a form here, so that way the data would presumably be available for the submit button's click event.
  • I'm also realizing that my current strategy of just writing the notes directly to the local storage with keys like note_0 and note_1 evaluating directly to those specific notes puts too much faith in my indexing system always being correct. Since these notes can be deleted and added by the user unpredictably, and the index doesn't really matter as far as persistence goes, it would make more sense to store the notes in a notes array that would be written to local storage.  I will do this tomorrow
I'm making good progress on the app, but I don't like how my object structure is looking right now.  I don't yet know what approach to take to make it better, though.

As of now, the delete button will delete the notes from local storage but does not update the ui by deleting the note list item.  As long as you make sure to reload the app each time you delete a note, it works as you would expect :).

I also added an empty note body css class, which is added to notes when the body is found to be empty.  I think it's better to do UI stuff in css over javascript, so I'll try to do that sort of thing more in the future.  I should probably switch my 'Untitled Note' heading if the title is left blank over to css as well.

Friday, May 15, 2015

5/15/15 - Text Overflow and Icon Fonts

Today I set up an icon font called Font Awesome, and worked on getting text overflow to work (which was harder than it seems, because you apparently need both overflow and text-overflow). My css ended up as
.note_title {
    /* ... */
    overflow: hidden;
    text-overflow: ellipsis;
}

 

The icon font was also super easy to set up.  I just added the font-awesome folder to my css/ and added a <link> tag. Then I can do something like <i class="fa fa-pencil"></i> which renders as the pencil icon on the header.

Tomorrow I will get the delete button to actually delete notes.

Thursday, May 14, 2015

5/14/15 - Adding Titles and Styling Upgrade

I did some more work on the NotesDemo stylesheet, and changed the toggle element from a button to a div.  It also switches between a - and a + depending on if the add note interface is visible or hidden.

Here are some screens that show the app functionality so far (read left to right)
I added a ui object to handle the ui updating, and separated the ui functionality that had previously been in the notes object. Now the notes object only handles translating the user input into the note html.

I still haven't figured out a good way to DRY up the selector class names, as they are all subject to change. I'll wait until the project gets a little more robust, and then making fixing that a high priority.

Since we're going to be live coding this in front of an audience, I'm not sure if I should be sacrificing style for ease of entry. For example, having everyone type in rgb(239, 109, 39) to get the toggle button's border color or #F6A031 to get the header background might become tedious.  Even though it looks bad, it might make more sense just to use the built in colors like red and blue. There's probably a happy medium somewhere.

Wednesday, May 13, 2015

5/13/15 - Notes App Javascript and More Styling

Today I started working on the javascript of the notes demo app.  One of the patterns in JavaScript: The Good Parts is to avoid using the new keyword by making constructor functions that create objects. I'm not sure if there are cases where you would want to use new over a constructor function, I'll have to keep reading the book (and re-read that section, as I'm not sure I fully understand it yet).

My new_notes_obj function returns an object that will contain the notes behavior. I'm not going to worry about decoupling this object from the display implementation (the css class names) just yet, because I'm not sure what form this app will take, and I don't want to get overly complex and beyond the scope of what's easy to teach in 6 hours. Of course, good code will probably also be easier to teach, so I'll come back to this to fix it up once I have a better idea of what the app will look like.

I will definitely keep working on the interface, especially the elements to add new notes.  There's no way to set note titles, and I'd also like for the textarea to increase in the number of rows as the user types more, so there's less scrolling involved.

I'm also running into some issues with naming css classes.  I'm not sure if it's ok to include the element type in the class name, like <textarea class="note_input_textarea"> instead of just <textarea class="note_input">

Tuesday, May 12, 2015

5/12/15 - Notes App

Today I worked on the basic styling for the notes app Chris and I will be developing live for the Firefox OS workshop. I ended today with this as the style, but it is all subject to change.

Here's my html and my css. Since these notes are eventually going to be generated by app.js, I went ahead and added most of the styles as classes.

Monday, May 11, 2015

Changes 5/11/15 - Changing a local git repo's upstream

Last Thursday, Chris helped me out with switching the LibriFox github from fccardiff/LibriFox to ahirschberg/LibriFox
 
$ git remote -v
origin  https://github.com/fccardiff/LibriFox.git (fetch)
origin  https://github.com/fccardiff/LibriFox.git (push) 
git remote is currently pointed to Finn's repo
$ git remote rm origin
$ git remote -v
no output, because the origin reference has been removed
$ git remote add origin https://github.com/ahirschberg/LibriFox.git
$ git remote -v
origin  https://github.com/ahirschberg/LibriFox.git (fetch)
origin  https://github.com/ahirschberg/LibriFox.git (push)
origin now points to the correct repo
$ git branch --set-upstream-to=origin/master master
Set up git so that it knows what you mean by $ git pull
$ git pull
Today, I worked on LibriFox a little more, then realized I should start on the notes app that Chris and I will be developing for the talk. I set up a repo for it, but didn't get much farther than that.

Thursday, May 7, 2015

5/7/15 - File Manager UI Improvements

In order to prepare for the Career Center Tech Expo, I hammered out some improvements to our rudimentary file interface.  Before, the app would display all the files on the (emulated) SD card, which for some reason included a bunch of temp files for the emulator.  I first added logic to only show files with paths that matched the regular expression /librifox\/.*/, which only shows files that have librifox/ in the path. Next, I refactored our enumeration handler by pulling all the file traversal logic into a base method that takes functions func_each for code to be executed for each match, func_done for code to be executed once all the files had been traversed, and func_error for any errors that occurred.

This is a really flexible way to implement an algorithm that has slight variations in how it works, as I can display all the librifox files in a list view and delete all librifox files with the exact same call pattern, just by varying the functions passed as func_each and func_done.

Also, because functions keep the scope they were originally called by, I was able to do the following to check whether any librifox files were present on the device: I'm really glad I'm reading JavaScript: the Good Parts :)

I thought that this was something unique to javascript, but it works in ruby as well! Neat!

Wednesday, May 6, 2015

5/6/15 - Website Updates

I updated my website for the first time in a while, and added user input to set the number of shakers.  Apparently, .cf domains are free, so I also got alexhirschberg.cf, which currently just redirects to that shakers page.

One problem with how the shakers are implemented now is that the shakers are fully regenerated each time the canvases update position on the page (which is different than the images 'shaking', that part is optimized well), which removes all the shaker canvases from the DOM and recreates them.  This is very resource intensive with higher canvases (~150+), so that there is noticeable flashing while the browser recreates them.  Once you get up to 1000, there is less shaking and more just flashing because of how badly optimized the regeneration is.  I'll have to work on that when I have free time.

5/6/15 - Javascript Quirks and More Testing

One place I noticed that my tests failed to do their job was when ensuring the correct arguments were passed to getBlob, as the mocked version returns the blob regardless of arguments. I had messed up the url variable in the BookStorageManager object, and was passing in book_object.url (undefined) instead of book_object.fullBookUrl (...url), but my tests were still succeeding, which is bad. I set up a spy for my mocked getBlob method so I could check what arguments it was being called with. One problem with this specific approach is that it relies really heavily on the ordering of arguments, which could be subject to change.

Given the object
{ 
  'progress_callback': req_progress_callback,
  'test_var':42
};
being passed into another object's function, and then logging the variables on both ends, I got:
LOG: 'args passed to object: '
LOG: Object{progress_callback: function (event) { ... }, test_var: 42}
LOG: 'args recieved by object:'
LOG: Object{progress_callback: undefined, test_var: 42}
This is really strange, and I guess the function goes out of scope when the object changes. I also changed the progress_callback to 'progress_callback': req_progress_callback ? 'true' : 'false' and got
LOG: 'args passed to object: '
LOG: Object{progress_callback: 'true', test_var: 42}
LOG: 'args recieved by object:'
LOG: Object{progress_callback: 'false', test_var: 42}
which is even stranger, because that means the ternary statement is being evaluated twice rather than just setting the value.

On the other hand, setting 'progress_callback': function () {} passes the function in correctly, so it looks like in javascript you just have to encapsulate any functions passed in arguments in functions defined within the object. Hopefully JavaScript: The Good Parts will talk about this. I don't like the idea of just encapsulating it with 'progress_callback': function (e) {callback(e)} and assuming there will only be 1 argument, because I don't necessarily want this function to depend on the interface of my HttpRequestHandler. StackOverflow says that you can get around this by doing function () {callback.apply(this, arguments)} which calls the function but allows you to set the two implicit variables declared in all javascript functions, this and arguments

I was getting some more weird test behavior, and was trying to debug it with no success, when I finally realized that my test for the progress callback were both using blobSpy.getCall(0), which refers to the first time the blob had been called. However, there was nothing clearing the list, so all tests would be looking at the results of whatever test happened to be run first. To fix this, I did
beforeEach(function () {
    blobSpy.reset();
});
In the future, I'll always check this when dealing with weird issues.

Here are my finished tests. Also, notice the comment here, which is a question for anyone who knows more about this than I do.

Do I need to test that the progress callback correctly updates the scrollbar as the file downloads? I think it's definitely possible to write a test that does this, but I feel like I'm 'wasting' a ton of time fighting with the DOM to be able to test my user interface, when maybe I would be more productive just leaving that part of it untested. For example, setting up the tests I talk about in this post took around 4 hours, just because I was fighting javascript the whole way to get it to do what I wanted. That being said, in writing these tests I found a bunch of bugs that I wouldn't have noticed otherwise, so it was definitely worth it in this case.

Friday, May 1, 2015

5/1/15 - Gross Test

There was one aspect of BookPlayerPageGenerator that I hadn't tested yet, and that was the syncing between the current position of the audio element and the relevant chapter object's position property.  This is set up as a timeupdate listener that sets the position property from currentTime.

I had no good way of testing this, so this is what I came up with (reproduced below)
This is a gross way to test chapter position updates, but I can't set the currentTime property unless the audio has actually loaded metadata (so that the element knows the source length), and I don't know if I should bother with it. Since currentTime is 0 by default (and can be called even without metadata loaded), this tests the behavior by setting the position to -1 and then checking to see if it has 'updated' to 0.

It's times like this that I realize I have no idea what I'm doing when it comes to testing HTML elements.

5/1/15 - Bash *

Today, I was trying to figure out where a console.log debug statement that I no longer needed was coming from. I asked Sam for a bit of help with grep and ended up learning a ton of interesting stuff about bash in the process. Here are the commands I did:

  1. $ grep --help | less - Pipes the help output to the less, which paginates whatever is passed into it so the console isn't spewing tons of text all at once.
  2. $ grep console.log js/app.js tests/* - From the help output, I learned that you can search in multiple files at once by listing them after the search string. Sam also explained how the * special character actually expands to all the files in the given scope and passes their paths into the program.
  3. I didn't believe him, so I wrote a quick ruby program:
    p ARGV (that's the whole program--I love ruby!) and ran $ args.rb *
    Sure enough, this outputted ['args.rb', 'arr.rb', ...] (also, 'args.rb' is included because it's a file in the scope of * and not because the run command was ruby args.rb ...)
  4. The problem with the grep command I used was that it didn't tell me the line numbers for the console.log matches. I looked through the help some more, and eventually ran $ grep console.log js/app.js tests/* -n.
    This outputted a nice list:
    js/app.js:164:        console.log(bookPath);
    js/app.js:176:                console.log('wrote: ' + this.result);
    js/app.js:334:                console.log("error loading json from url " + url);
    js/app.js:335:                console.log(e);
    js/app.js:338:                console.log("timeout loading json from url " + url);
    js/app.js:339:                console.log(e);
    
    and I was able to identify the line I wanted gone.
  5. So today I learned: bash and grep are pretty neat!