I started working with Mongo again recently and needed to fiddle a bit to retrieve existing data using the official 10gen driver.
Let's say you have the following persisted in a Mongo collection:
{ "_id": ObjectId("4efa88..."), "first_name": "Scott" };
The driver will happily work with an object of the following type using the default mapping conventions.
public class Person { public ObjectId _id { get; set; } public string first_name { get; set; } }
Of course, the C# class doesn't follow the CLR convention of using PascalCase for property names. Fortunately, you can create custom naming conventions for the driver.
class NameConvention : IElementNameConvention { public string GetElementName(MemberInfo member) { return member.Name.ToLower(); } }
Then register the custom convention during application startup.
var conventions = new ConventionProfile(); conventions.SetElementNameConvention(new NameConvention()); BsonClassMap.RegisterConventions(conventions, t => t.Namespace.StartsWith("MyApp"));
And now you can have as many capital letters as you want in the property name (and LINQ queries with FluentMongo still work).
public class Person { public ObjectId _id { get; set; } public string First_Name { get; set; } }
A few people have asked me how to keep their cascading style sheets under control. This isn't a question related to LESS and SASS, although both of them can help build maintainable styles, but more of a question about how to keep a team from hacking a stylesheet until it grows to rival the complexity of the United States tax code.
Here are a few thoughts.
Even if you don't use a framework in your product, you can learn from one, and borrow ideas. Over the last couple years I've worked with Bootstrap and 960gs, as well as the CSS themes provided by jQuery UI and jQuery Mobile. I'd consider Bootstrap and 960gs to be more along the lines of "full" frameworks because they cover everything including resets, layout and typography for an entire application. jQuery UI's CSS leans more towards working in the background to support UI widgets, while jQM is a bit of both.
Regardless of which framework you look at, you'll see how they define well-factored, reusable classes. For example, a vertical navigation bar is something you build with Bootstrap by applying three classes:
<ul class="nav nav-pills nav-stacked"> <li class="active">Home</li> <li>About</li> </ul>
Although they could provide a single class definition to achieve the same effect, the single class would be harder to reuse for other scenarios. It's almost as if the style definitions in these frameworks follow a single responsibility principle. A header bar in a jQM application uses two class definitions: one to make a header and one to make a bar.
<div class="ui-header ui-bar-f" data-role="header"> ... </div>
Having generic, reusable styles means you can combine styles together to create new things instead of inventing new styles, which leads to #2.
For some projects the number of distinct classes you can find in a stylesheet is overwhelming. It's easier for developers to create new styles than try to figure out how to use the existing styles. Part of the problem here goes back to #1 and the single responsibilities, but part of the problem is the combination of each developer writing unique markup and not using what CSS selectors can offer.
For example, consider the following markup using 3 different classes:
<div class="product"> <hgroup> <h3 class="productTitle">@product.Name</h3> <h4 class="productSubtitle">@product.Subtitle</h4> </hgroup> ... </div>
Let's change the CSS around so only a single class is required, and only on the top level element.
.product { background-color: blanchedalmond; } .product hgroup > h3 { background-color: darkmagenta; ... } .product hgroup > h4 { color: black; ... }
Fewer named styles mean there are fewer concepts to grasp when learning how to use the styles.
When I work on a project with a dedicated designer there is rarely a problem with stylesheets. It's not that the designer has a fantastic knowledge of CSS (they usually do), but just having someone who cares about the structure and organization of the CSS helps keep it in shape.
It's the large projects with no designer where the CSS grows unwieldy, because styles are a low priority item. It only takes one person who cares enough and monitors the use of styles to bring up issues early before the issues turn into problems.
Other thoughts?
It's always been difficult to have nicely formatted source code in a PowerPoint slide, but with HTML it's fairly easy to paste code into a pre block.
<pre> <code> var start = function () { assignSlideIdentifiers(); setFirstVisibleSlide(); $(window).bind("keydown", onKeyDown) .bind("hashchange", hashChange); }; </code> </pre>
Of course there is no syntax highlighting, but that's easy to rectify with google-code-prettify, a JavaScript library to provide syntax highlighting for web pages. The library supports JavaScript, C#, CSS, HTML, Ruby, and a host of other languages.
Once the prettify CSS and script is in place, all that is needed is a little kick start to prettify the code before the slide show starts. The library looks for a class of prettyprint before it jumps into action, so the startup code adds the class to code elements by default.
$(function () { $("code").addClass("prettyprint"); prettyPrint(); p5.start(); });
The library figures out the code you are using by sniffing for keywords and lexing with regular expressions (the source is a regex showcase). There are CSS classes you can apply if the library guesses the wrong language.
I promised to end this series after 10 posts, but I've already had requests to continue and add more features. Perhaps we'll continue after a break. This was a fun little exercise and was actually my second iteration of making an HTML 5 slide presenter.
Here are links to the previous posts: Part 1, Part 2, Part 3, Part 4, Part 5, Part 6, Part 7, Part 8, Part 9.
And here is a list of the libraries / frameworks being used:
- jQuery
- Zoomooz
For all the code, see github.
I had very simple requirements for the zoom and pan style animations. I wanted the animations to work by zooming from element to element based on the presence of a data- attribute (indicating step 1, step 2 . . . step N).
<section data-animation="zoom"> <h1>Zooming to Elements</h1> <div data-zoom-step="1"> <img src="images/colorful.jpg" /> </div> <div data-zoom-step="3" style="-webkit-transform: rotate(10deg);"> <p>Random words</p> </div> <div data-zoom-step="2" > <p>Vertical words</p> </div> </section>
I also wanted the animations to rotate the viewport to match the rotation of a zoomed element. To see the final product, you can watch the following clip:
I thought my requirements were simple, but it's always the simple requirements that sneak up on you...
To make a long story a bit shorter, there are several things I learned in trying to hack out my own zoom and pan features.
1. getBoundingClientRect, part of the CSSOM View Model, is particularly useful for finding the precise boundaries of any given element.
2. Also useful is window.getComputedStyle, because getComputedStyle can give back a CSS transformation matrix which is easier to manipulate than the long-winded form of a CSS transform. In other words, given the following CSS . . .
#target { -webkit-transform: rotateZ(10deg) scale(0.8) translateX(10px); }
. . . then the following code . . .
var styles = window.getComputedStyle(target); var transform = styles["-webkit-transform"];
. . . will give you a value in transform like [1]:
"matrix(0.7878, 0.1389, -0.1389, 0.7878, 7.8784, 1.389)"
From there you'd think everything is just multiplication and addition [2], but by the time you factor in the vendor prefixes, scroll offsets, aesthetic padding and easing, and how coordinates change as the viewport transforms, then the edge cases cut like knives.
Zoomooz is an incredibly easy to use jQuery plugin for zooming to any element on a page. Once I found Zoomooz I turned over all responsibility for zooming, panning, and rotating to the library and focused on shimming it into my slideshow framework. After spending a few hours fiddling with stuff in the above section, the following code took 10 minutes or so.
var zoom = function (element) { var target = element; var signal = completionSignal(); var zoomables = _.sortBy( target.children("[data-zoom-step]"), function (child) { return -($(child).data().zoomStep); } ); var step = function () { var child = $(zoomables.pop()); if (child.length > 0) { child.zoomTo(child.data()); } else { signal.resolve(); } }; var reset = function () { $("body").zoomTo({ targetsize: 1.0 }); }; signal.promise.always(reset); return { step: step, done: signal.promise }; };
The code plugs into the animation infrastructure described in part 8. During initialization the code finds all the data-zoom-step elements and orders them into an array using Underscore. The zoomTo method is provided by Zoomooz.js. The code also uses the completion signal handler (also discussed in part 8) to register clean up code and announce when all zoomable elements have been visited and the animation is complete.
Although this isn't everything needed to pull off the animations, you can find all the source code on github.
Next up: pretty printing code.
[1] Least significant digits removed.
[2] Also see Sylvester.
Popcorn.js is a JavaScript library for working with HTML 5 video and audio in a web page. It's part of Mozilla's Popcorn project, and it already has a bonanza of plugins available.
Part of Popcorn's job is to smooth over API differences in the <audio> and <video> programming interface, but the real strength of Popcorn and its plugins is in combining and orchestrating content associated with a video.
Popcorn is a library for storytellers.
For example, while a video is playing on a page, you want to display a welcome message from 0 to 8 seconds, and an interactive map of where the video is filmed from 10 to 20 seconds. With Popcorn this is as easy as:
var pop = Popcorn("#video") .footnote({ "start": 0, "end": 8, "target": "content", "text": "Welcome!" }).googlemap({ "start": 10.0, "end": 20, "target": "content", "type": "TERRAIN", "zoom": 16, "lat": 39.665389, "long": -105.205698, "location": "Red Rocks" }); pop.play();
Other plugins provide support for YouTube, Vimeo, Twitter, Flickr photos, Twitter streams, Facebook, and more. Popcorn is easy (and fun)!
When I started thinking about adding animations to the slide presenter I pictured a few different types of animations – simple animations to make items appear one by one, but also animations to pan, zoom, and rotate a slide (like Prezi, which is Flash based, but really more like Impress.js, which is all script and CSS).
I wanted animations to be decoupled and extensible, yet still give the slide presenter control over the animations. When an animation like "appear one by one" is in effect, then a "move forward" operation shouldn't go to the next slide, but should step the animation forward to make an item appear. Once all the items are on the screen then "move forward" can advance the slide. A "move back" should cancel any running animations and go to the previous slide.
It's funny – as I was thinking of these features I thought about eventing with jQuery trigger and completion signals with jQuery promises. Then I thought I better keep it simple and plowed ahead using function callbacks for everything.
My unit tests told me the design wasn't working. When small changes make tests break and feel brittle, it is a general indication of trouble – like a warning bell.
With YAGNI disproved I was back to promises and triggers. At the heart of the matter is a completionSignal. All the animation modules rely on completionSignal to broadcast when the animation is finished, and tell them if the animation needs to stop early (the presenter triggers the killAnimations event if the slides need to jump backwards, to the home, or to the end).
var completionSignal = function () { var deferred = $.Deferred(); var complete = function () { $(window).unbind("killAnimations", complete); deferred.resolve(); }; $(window).bind("killAnimations", complete); return { resolve: complete, promise: deferred.promise() }; };
The beauty of a deferred object, which typically represents an asynchronous operations, is how other components can register zero or more functions into callback queues via the object's promise. When the deferred object is resolved (successful completion) or rejected (failed), the object invokes the proper callbacks. We'll see an example in just a bit, but for more, see "Creating Responsive Applications Using jQuery Deferred and Promises". For custom events and trigger see "Event Aggregation with jQuery".
To see how animation works in the slide presenter, here is a simple animation that will pop up an alert box for the current slide before allowing the presentation to move to the next slide. Animations are added using data- attributes.
<section> <h1>This is the title</h1> <ul> <li data-animation="alert">Show this in a popup</li> <li>More text</li> </ul> </section>
The slide presenter finds the data-animation attributes, then looks up a module to call using the value of the attribute. Multiple animations modules can register with the slide show software.
p5.registerAnimations({ "onebyone": onebyone, "timedAppear": timedAppear, "zoom": zoom, "addclass": addclass, "alert": annoying });
The "alert" animation is aliased from a module named "annoying" (because any kind of modal dialog is annoying). The annoying module uses a signalCompletion to help with lifetime events.
var annoying = function (element) { var signal = completionSignal(); return { step: function () { alert(element.text()); signal.resolve(); }, done: signal.promise }; };
The public API exposed from any animation requires a step method and done property. The slide show software is responsible for invoking the step method when the slide show tries to move forward. The animation is responsible for resolving the "done" promise when the animation is complete. The animation can also use the signal to register cleanup code (none needed in this example). The presenter software uses the promise with jQuery's $.when to remove completed animations from it's call queue.
$.when(animation.done)
.then(function () {
currentAnimations.remove(animation);
});
I'll try to wrap it up next week by looking at only two more pieces: formatting code in a slide, and rotating, panning, and zooming with CSS 3 transforms.
For all the code, see github.
When starting into the animations I decided it was time to refactor. One problem was the little "updateState" calls floating around inside conditional logic – always a bad sign. A second problem was the sheer number of strings and CSS selectors in the code.
I've become more sensitive to primitive obsession in JavaScript. Some code I see is nothing but strings and numbers. I think the obsession hurts readability and maintainability.
The selectors scattered in the slide presenter code do one of three things: get all slides, get the next slide, and get the previous slide. It would be nice to hide all of these behind tiny function abstractions.
var slides = function () { return $("section"); }; var nextSlide = function () { return $("section.current").next("section"); }; var previousSlide = function () { return $("section.current").prev("section"); };
Those functions make the logic for moving around much easier to read.
var moveForward = function () { setCurrentSlide(nextSlide()); }; var moveBackward = function () { setCurrentSlide(previousSlide()); }; var moveFirst = function () { setCurrentSlide(slides().first()); }; var moveLast = function () { setCurrentSlide(slides().last()); };
As a bonus, all of the book keeping logic for setting the current slide moves into a single location.
var setCurrentSlide = function (slide) { if (slide.length > 0) { slides().removeClass("current"); slide.addClass("current"); updateHistory(slide); } };
Having a central location to set the currently selected slides is important for the upcoming animation logic.
For all the code, see github.