Integrating with jshint, adding destination controls, and additional minor fixes.
Change-Id: I72eb61fb9e9d7031ffb69d989ebf388cb3193783
diff --git a/.jshintignore b/.jshintignore
new file mode 100644
index 0000000..dac66df
--- /dev/null
+++ b/.jshintignore
@@ -0,0 +1,3 @@
+ifc
+node_modules
+server-root
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..16f23d1
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,29 @@
+{
+ "camelcase": true,
+ "curly": true,
+ "eqeqeq": true,
+ "expr": true,
+ "forin": true,
+ "freeze": true,
+ "immed": true,
+ "indent": 2,
+ "latedef": "nofunc",
+ "maxlen": 80,
+ "newcap": true,
+ "noarg": true,
+ "nonbsp": true,
+ "nonew": true,
+ "quotmark": "single",
+ "sub": true,
+ "trailing": true,
+ "undef": true,
+ "unused": "vars",
+
+ "browser": true,
+ "devel": true,
+ "node": true,
+
+ "globals": {
+ "window": true
+ }
+}
diff --git a/Makefile b/Makefile
index ca463ca..90a9d76 100644
--- a/Makefile
+++ b/Makefile
@@ -42,8 +42,12 @@
@cp $< $@
@echo "Copying static file $<"
+.PHONY: lint
+lint: node_modules
+ @jshint .
+
.PHONY: test
-test: $(tests)
+test: lint $(tests)
.PHONY: $(tests)
$(tests): test/%: test/%.js test/* mocks/* ifc node_modules $(js_files)
diff --git a/mocks/google-maps.js b/mocks/google-maps.js
index 2a25bc8..272aab7 100644
--- a/mocks/google-maps.js
+++ b/mocks/google-maps.js
@@ -1,5 +1,5 @@
-var $ = require('../src/util/jquery')
-var defineClass = require('../src/util/define-class')
+var $ = require('../src/util/jquery');
+var defineClass = require('../src/util/define-class');
var ControlPosition = {
TOP_LEFT: 'tl',
@@ -11,7 +11,7 @@
this.$ = $('<div>');
this.$.appendTo(parent);
},
-
+
publics: {
push: function(child) {
this.$.append(child);
@@ -27,11 +27,11 @@
},
LatLng: function(){},
ControlPosition: ControlPosition,
-
+
places: {
SearchBox: function(){}
},
-
+
event: {
addListener: function(){}
}
diff --git a/mocks/vanadium.js b/mocks/vanadium.js
index 815df95..f7a978f 100644
--- a/mocks/vanadium.js
+++ b/mocks/vanadium.js
@@ -3,16 +3,17 @@
var MockRuntime = defineClass({
publics: {
on: function(event, handler) {
- if (event == 'crash')
- this.crash.add(handler);
+ if (event === 'crash') {
+ this.onCrash.add(handler);
+ }
},
fireCrash: function(err) {
- this.crash(err);
+ this.onCrash(err);
}
},
-
+
events: {
- crash: 'private'
+ onCrash: 'private'
}
});
@@ -20,18 +21,18 @@
init: function(t) {
this.t = t;
},
-
+
publics: {
init: function(config, callback) {
this.t.ok(config, 'has config');
this.callback = callback;
},
-
+
finishInit: function(err, runtime) {
this.callback(err, runtime);
}
},
-
+
statics: {
vlog: {
levels: {
diff --git a/package.json b/package.json
index 7ed8b4c..1c4c638 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"devDependencies": {
"browserify": "^10.2.4",
"jsdom": "^3.1.2",
+ "jshint": "^2.8.0",
"node-static": "^0.7.6",
"tape": "^4.0.0"
},
diff --git a/src/components/destination.js b/src/components/destination.js
new file mode 100644
index 0000000..7d0e411
--- /dev/null
+++ b/src/components/destination.js
@@ -0,0 +1,48 @@
+var $ = require('../util/jquery');
+var defineClass = require('../util/define-class');
+
+var Destination = defineClass({
+ publics: {
+ setSearchBounds: function(bounds) {
+ this.searchBox.setBounds(bounds);
+ },
+
+ setPlaceholder: function(placeholder) {
+ this.$searchBox.attr('placeholder', placeholder);
+ }
+ },
+
+ events: [
+ /**
+ * @param places (array of places)
+ */
+ 'onSearch'
+ ],
+
+ constants: ['$'],
+
+ init: function(maps, placeholder, initial) {
+ var destination = this;
+
+ var $searchBox = $('<input>')
+ .attr('type', 'text');
+ this.$searchBox = $searchBox;
+
+ this.setPlaceholder(placeholder);
+
+ if (initial) {
+ $searchBox.prop('value', initial);
+ }
+
+ this.$ = $('<div>').addClass('destination')
+ .append($searchBox);
+
+ this.searchBox = new maps.places.SearchBox($searchBox[0]);
+
+ maps.event.addListener(this.searchBox, 'places_changed', function() {
+ destination.onSearch(destination.searchBox.getPlaces());
+ });
+ }
+});
+
+module.exports = Destination;
\ No newline at end of file
diff --git a/src/components/destinations.js b/src/components/destinations.js
new file mode 100644
index 0000000..088e02c
--- /dev/null
+++ b/src/components/destinations.js
@@ -0,0 +1,76 @@
+var $ = require('../util/jquery');
+var defineClass = require('../util/define-class');
+
+var strings = require('../strings').currentLocale;
+
+var Destination = require('./destination');
+
+var Destinations = defineClass({
+ publics: {
+ append: function(destinationName) {
+ var placeholder;
+ switch (this.destinations.length) {
+ case 0:
+ placeholder = strings['Origin'];
+ break;
+ case 1:
+ placeholder = strings['Destination'];
+ break;
+ case 2:
+ this.destinations[1].setPlaceholder(strings.destination(1));
+ /* falls through */
+ default:
+ placeholder = strings.destination(this.destinations.length);
+ }
+
+ var destination = this.addDestination(placeholder, destinationName);
+ this.$.append(destination.$);
+ this.destinations.push(destination);
+ },
+
+ /**
+ * @param handler callback receiving a <code>Destination</code> instance
+ * each time a <code>Destination</code> is added. On initial add, the
+ * callback is called with all current <code>Destination</code>s.
+ */
+ addDestinationBindingHandler: function(handler) {
+ this.onDestinationAdded.add(handler);
+ $.each(this.destinations, function(i, destination) {
+ handler(destination);
+ });
+ }
+ },
+
+ privates: {
+ addDestination: function(placeholder, destinationName) {
+ var destination = new Destination(this.maps, placeholder,
+ destinationName);
+ this.onDestinationAdded(destination);
+ return destination;
+ }
+ },
+
+ events: {
+ /**
+ * @param destination Destination instance
+ */
+ onDestinationAdded: 'private'
+ },
+
+ constants: ['$'],
+
+ init: function(maps, initial) {
+ this.maps = maps;
+ this.$ = $('<form>').addClass('destinations');
+
+ this.destinations = [];
+
+ initial = initial || [];
+
+ for (var i = 0; i < Math.max(initial.length, 2); i++) {
+ this.append(initial[i]);
+ }
+ }
+});
+
+module.exports = Destinations;
\ No newline at end of file
diff --git a/src/components/maps.js b/src/components/maps.js
index 3e52c98..122a628 100644
--- a/src/components/maps.js
+++ b/src/components/maps.js
@@ -1,8 +1,7 @@
-var global = require('global');
var $ = require('../util/jquery');
var defineClass = require('../util/define-class');
-var strings = require('../strings')();
+var Destinations = require('./destinations');
var Messages = require('./messages');
var Widget = defineClass({
@@ -14,66 +13,145 @@
marker.setMap(null);
});
},
-
+
+ closeActiveInfoWindow: function() {
+ if (this.activeInfoWindow) {
+ this.activeInfoWindow.close();
+ }
+ this.activeInfoWindow = null;
+ },
+
message: function(message) {
this.messages.push(message);
}
},
-
- constants: ['$'],
-
+
+ privates: {
+ destinationSelectionWindow: defineClass.innerClass({
+ privates: {
+ renderInfo: function() {
+ var $info = $('<div>').addClass('destination-info');
+
+ $info.append($('<div>')
+ .addClass('title')
+ .text(this.place.name));
+
+ return $info[0];
+ }
+ },
+
+ init: function(place, createMarker) {
+ var widget = this.outer;
+ var maps = widget.maps;
+ var map = widget.map;
+
+ this.place = place;
+
+ var infoWindow = new maps.InfoWindow({
+ content: this.renderInfo(),
+ position: place.geometry.location
+ });
+
+ var marker;
+ if (createMarker) {
+ marker = new maps.Marker({
+ map: map,
+ title: place.name,
+ position: place.geometry.location
+ });
+
+ maps.event.addListener(marker, 'click', function() {
+ widget.setActiveInfoWindow(infoWindow, marker);
+ });
+
+ widget.markers.push(marker);
+ } else {
+ widget.setActiveInfoWindow(infoWindow);
+ }
+ }
+ }),
+
+ setActiveInfoWindow: function(infoWindow, marker) {
+ this.closeActiveInfoWindow();
+ this.activeInfoWindow = infoWindow;
+ infoWindow.open(this.map, marker);
+ },
+
+ centerOnCurrentLocation: function() {
+ var maps = this.maps;
+ var map = this.map;
+
+ // https://developers.google.com/maps/documentation/javascript/examples/map-geolocation
+ if (global.navigator && global.navigator.geolocation) {
+ global.navigator.geolocation.getCurrentPosition(function(position) {
+ map.setCenter(new maps.LatLng(position.coords.latitude,
+ position.coords.longitude));
+ });
+ }
+ },
+
+ bindDestinationControl: function (destination) {
+ var widget = this;
+ var maps = this.maps;
+ var map = this.map;
+
+ maps.event.addListener(map, 'bounds_changed', function() {
+ destination.setSearchBounds(map.getBounds());
+ });
+
+ destination.onSearch.add(function(places) {
+ widget.clearMarkers();
+ widget.closeActiveInfoWindow();
+ var bounds = new maps.LatLngBounds();
+
+ if (places.length === 1) {
+ var place = places[0];
+ widget.destinationSelectionWindow(place, false);
+
+ map.setCenter(place.geometry.location);
+ } else if (places.length > 1) {
+ $.each(places, function(i, place) {
+ widget.destinationSelectionWindow(place, true);
+ bounds.extend(place.geometry.location);
+ });
+
+ map.fitBounds(bounds);
+ }
+ });
+ }
+ },
+
+ constants: ['$', 'maps'],
+
// https://developers.google.com/maps/documentation/javascript/tutorial
init: function(maps) {
- maps = maps || global.google.maps;
- var widget = this;
-
+ this.maps = maps = maps || global.google.maps;
+
this.$ = $('<div>').addClass('map-canvas');
-
+
this.markers = [];
+ this.route = {};
+
this.messages = new Messages();
-
+ this.destinations = new Destinations(maps);
+
var config = {
zoom: 11,
center: new maps.LatLng(37.4184, -122.0880) //Googleplex
};
-
+
var map = new maps.Map(this.$[0], config);
-
- // https://developers.google.com/maps/documentation/javascript/examples/map-geolocation
- if (global.navigator && global.navigator.geolocation) {
- global.navigator.geolocation.getCurrentPosition(function(position) {
- map.setCenter(new maps.LatLng(position.coords.latitude,
- position.coords.longitude));
- });
- }
-
+ this.map = map;
+
+ this.centerOnCurrentLocation();
+
var controls = map.controls;
-
- var $searchBox = $('<input>')
- .attr('type', 'text')
- .attr('placeholder', strings['Search']);
- var txtSearchBox = $searchBox[0];
- controls[maps.ControlPosition.TOP_LEFT].push(txtSearchBox);
-
+
+ this.destinations.addDestinationBindingHandler(
+ $.proxy(this, 'bindDestinationControl'));
+
+ controls[maps.ControlPosition.TOP_LEFT].push(this.destinations.$[0]);
controls[maps.ControlPosition.TOP_CENTER].push(this.messages.$[0]);
-
- var searchBox = new maps.places.SearchBox(txtSearchBox);
-
- maps.event.addListener(map, 'bounds_changed', function() {
- searchBox.setBounds(map.getBounds());
- });
-
- maps.event.addListener(searchBox, 'places_changed', function() {
- var places = searchBox.getPlaces();
- if (places.length == 1) {
- var place = places[0];
- widget.markers.push(new maps.Marker({
- map: map,
- title: place.name,
- position: place.geometry.location
- }));
- }
- });
}
});
diff --git a/src/components/message.js b/src/components/message.js
index a96e7cd..30248f9 100644
--- a/src/components/message.js
+++ b/src/components/message.js
@@ -7,49 +7,53 @@
module.exports = {
INFO: INFO,
ERROR: ERROR,
-
+
info: function(text) {
return {
type: INFO,
text: text
};
},
-
+
error: function(text) {
return {
type: ERROR,
text: text
};
},
-
+
Message: defineClass({
publics: {
setType: function(type) {
- if (type == INFO) {
- this.$.attr('class', 'info');
- } else if (type == ERROR) {
- this.$.attr('class', 'error');
- } else {
- throw 'Invalid message type ' + type;
+ switch (type) {
+ case INFO:
+ this.$.attr('class', 'info');
+ break;
+ case ERROR:
+ this.$.attr('class', 'error');
+ break;
+ default:
+ throw 'Invalid message type ' + type;
}
},
-
+
setText: function(text) {
this.$.text(text);
},
-
+
set: function(message) {
this.setType(message.type);
this.setText(message.text);
}
},
-
+
constants: ['$'],
-
+
init: function(initial) {
this.$ = $('<li>');
- if (initial)
+ if (initial) {
this.set(initial);
+ }
}
})
};
diff --git a/src/components/messages.js b/src/components/messages.js
index 55eef83..f532df3 100644
--- a/src/components/messages.js
+++ b/src/components/messages.js
@@ -29,9 +29,9 @@
});
}
},
-
+
constants: ['$'],
-
+
init: function() {
this.$ = $('<ul>').addClass('messages');
}
diff --git a/src/debug.js b/src/debug.js
index 97c15f5..26f3446 100644
--- a/src/debug.js
+++ b/src/debug.js
@@ -1,5 +1,3 @@
-var global = require('global');
-
/**
* Global variable exports for console debug.
*/
diff --git a/src/identity.js b/src/identity.js
index 762fdb4..27ba0da 100644
--- a/src/identity.js
+++ b/src/identity.js
@@ -1,5 +1,3 @@
-'use strict';
-
var uuid = require('uuid');
module.exports = Identity;
@@ -8,22 +6,24 @@
this.username = extractUsername(accountName);
this.deviceType = 'desktop';
this.deviceId = uuid.v4();
-
+
this.deviceName = this.deviceType + '_' + this.deviceId;
this.entityName = this.username + '/' + this.deviceName;
-};
+}
function autoUsername() {
return uuid.v4();
}
function extractUsername(accountName) {
- if (!accountName || accountName === 'unknown')
+ if (!accountName || accountName === 'unknown') {
return autoUsername();
-
+ }
+
var parts = accountName.split('/');
- if (parts[0] !== 'dev.v.io' || parts[1] !== 'u')
+ if (parts[0] !== 'dev.v.io' || parts[1] !== 'u') {
return accountName;
-
+ }
+
return parts[2];
}
diff --git a/src/index.js b/src/index.js
index e304925..3efeeeb 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,8 +1,11 @@
var $ = require('./util/jquery');
var Travel = require('./travel');
var debug = require('./debug');
+var strings = require('./strings').currentLocale;
//http://api.jquery.com/ready/
$(function() {
+ //http://stackoverflow.com/questions/180103/jquery-how-to-change-title-of-document-during-ready/11171548#11171548
+ $('html head title').text(strings['Travel Planner']);
debug(new Travel());
});
\ No newline at end of file
diff --git a/src/static/index.css b/src/static/index.css
index 358500e..e6f76af 100644
--- a/src/static/index.css
+++ b/src/static/index.css
@@ -3,6 +3,21 @@
font-family: Arial, sans-serif;
}
+.destinations {
+ margin: 2em 3em;
+ width: 30em;
+}
+
+.destination {
+ width: 100%;
+}
+
+.destination input {
+ width: 100%;
+ font-size: 15px;
+ padding: 8px 16px;
+}
+
.map-canvas {
width: 100%;
height: 100%;
diff --git a/src/static/index.html b/src/static/index.html
index 2f50623..6d0be65 100644
--- a/src/static/index.html
+++ b/src/static/index.html
@@ -1,7 +1,7 @@
<html>
<head>
<meta charset="utf-8">
- <title>Google Travel</title>
+ <title>Travel Planner</title>
<link rel="stylesheet" type="text/css" href="index.css">
<script type="text/javascript"
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAQCvKWEWcSuQE2DSjVbvMKETSgF6S9i1k&signed_in=true&libraries=places">
diff --git a/src/strings.js b/src/strings.js
index 76bb6bb..d4e99fe 100644
--- a/src/strings.js
+++ b/src/strings.js
@@ -1,5 +1,14 @@
-module.exports = function() {
+function getStrings(locale) {
return {
- 'Search': 'Search'
+ 'Destination': 'Destination',
+ destination: function(n) {
+ return 'Destination ' + n;
+ },
+ 'Origin': 'Origin',
+ 'Travel Planner': 'Travel Planner'
};
-};
\ No newline at end of file
+}
+
+getStrings.currentLocale = getStrings();
+
+module.exports = getStrings;
\ No newline at end of file
diff --git a/src/travel.js b/src/travel.js
index 7f4f8b9..d6f864b 100644
--- a/src/travel.js
+++ b/src/travel.js
@@ -9,36 +9,36 @@
var TravelSync = require('./travelsync');
var Identity = require('./identity');
-var strings = require('./strings')(/* TODO: locale */);
-
var Travel = defineClass({
publics: {
error: function (err) {
this.maps.message(message.error(err.toString()));
},
-
+
info: function (info) {
this.maps.message(message.info(info));
}
},
-
+
init: function (opts) {
opts = opts || {};
var vanadiumWrapper = opts.vanadiumWrapper || vanadiumWrapperDefault;
var travel = this;
-
+
this.sync = new TravelSync();
-
- var reportError = $.proxy(this, 'error')
-
+
+ var reportError = $.proxy(this, 'error');
+
vanadiumWrapper.init(opts.vanadium).then(
function(wrapper) {
+ wrapper.onCrash.add(reportError);
+
var identity = new Identity(wrapper.getAccountName());
identity.mountName = makeMountName(identity);
- travel.sync.start(identity.mountName, wrapper).fail(reportError);
+ travel.sync.start(identity.mountName, wrapper).catch(reportError);
}, reportError);
-
- this.maps = new Maps(opts.maps);
+
+ this.maps = new Maps(opts.maps);
var $domRoot = opts.domRoot? $(opts.domRoot) : $('body');
$domRoot.append(travel.maps.$);
}
diff --git a/src/travelsync.js b/src/travelsync.js
index 293c755..d2217b5 100644
--- a/src/travelsync.js
+++ b/src/travelsync.js
@@ -7,7 +7,7 @@
init: function() {
this.tripPlan = [];
this.tripStatus = {};
-
+
// TODO: sync initial state
this.server = new vdlTravel.TravelSync();
diff --git a/src/util/define-class.js b/src/util/define-class.js
index 2a48279..8df6c29 100644
--- a/src/util/define-class.js
+++ b/src/util/define-class.js
@@ -34,11 +34,13 @@
* time. (As such, using that mechanism to declare private static constants does
* work.)
*/
-module.exports = function defineClass(def) {
+module.exports = defineClass;
+
+function defineClass(def) {
var constructor = function() {
var pthis = $.extend({}, def.privates, def.publics, def.statics);
var ifc = this;
-
+
if (def.events) {
if ($.isArray(def.events)) {
$.each(def.events, function(i, event) {
@@ -52,27 +54,44 @@
defineEventsFromObject(pthis, ifc, def.events);
}
}
-
- if (def.statics)
+
+ if (def.statics) {
$.extend(ifc, def.statics);
-
- if (def.init)
+ }
+
+ if (def.init) {
def.init.apply(pthis, arguments);
-
- if (def.publics)
+ }
+
+ if (def.publics) {
polyProxy(ifc, pthis, def.publics);
-
+ }
+
if (def.constants) {
$.each(def.constants, function(i, constant) {
ifc[constant] = pthis[constant];
});
}
};
-
- if (def.statics)
+
+ if (def.statics) {
$.extend(constructor, def.statics);
-
+ }
+
return constructor;
+}
+
+defineClass.innerClass = function(def) {
+ var init = def.init;
+ def.init = function(outer, constructorArgs) {
+ this.outer = outer;
+ init.apply(this, constructorArgs);
+ };
+
+ var InnerClass = defineClass(def);
+ return function() {
+ return new InnerClass(this, arguments);
+ };
};
function polyProxy(proxy, context, members) {
@@ -84,8 +103,9 @@
function filterProxy(proxy, context, nameFilter) {
$.each(context, function(name, member) {
- if (nameFilter(name))
+ if (nameFilter(name)) {
proxy[name] = $.proxy(member, context);
+ }
});
return proxy;
}
@@ -95,15 +115,16 @@
//Use polyProxy on function that fires to add the callable syntactic sugar
var callableDispatcher = pthis[name] =
polyProxy($.proxy(dispatcher, 'fire'), dispatcher, dispatcher);
-
- if (flags && flags.indexOf('private') > -1)
+
+ if (flags && flags.indexOf('private') > -1) {
return;
-
+ }
+
if (flags && flags.indexOf('public') > -1) {
ifc[name] = callableDispatcher;
} else {
ifc[name] = filterProxy({}, dispatcher, function(name) {
- return name != 'fire' && name != 'fireWith';
+ return name !== 'fire' && name !== 'fireWith';
});
}
}
diff --git a/src/vanadium-wrapper.js b/src/vanadium-wrapper.js
index 2774a7b..ceff029 100644
--- a/src/vanadium-wrapper.js
+++ b/src/vanadium-wrapper.js
@@ -6,75 +6,62 @@
var VanadiumWrapper = defineClass({
init: function(runtime) {
this.runtime = runtime;
- runtime.on('crash', this.crash);
+ runtime.on('crash', this.onCrash);
},
-
+
publics: {
getAccountName: function() {
return this.runtime.accountName;
},
-
+
/**
* @param endpoint Vanadium name
* @returns a promise resolving to a client or rejecting with an error.
*/
client: function(endpoint) {
var client = this.runtime.newClient();
- var async = $.Deferred();
- client.bindTo(this.runtime.getContext(), endpoint, function(err, client) {
- if (err)
- async.reject(err);
- else
- async.resolve(client);
- });
-
- return async.promise();
+ return client.bindTo(this.runtime.getContext(), endpoint);
},
-
+
/**
* @param endpoint Vanadium name
* @param server object implementing server APIs
* @returns a promise resolving to void or rejecting with an error.
*/
- server: function(endpoint, server, callback) {
- var async = $.Deferred();
- this.runtime.newServer().serve(endpoint, server, function(err) {
- if (err)
- async.reject(err);
- else
- async.resolve();
- });
- return async.promise();
+ server: function(endpoint, server) {
+ return this.runtime.newServer().serve(endpoint, server);
}
},
-
+
events: {
- crash: 'memory'
+ onCrash: 'memory'
}
});
module.exports = {
/**
* @param vanadium optional vanadium override
- * @returns a promise resolving to a VanadiumWrapper or rejecting with an error.
+ * @returns a promise resolving to a VanadiumWrapper or rejecting with an
+ * error.
*/
init: function(vanadium) {
vanadium = vanadium || vanadiumDefault;
var config = {
logLevel: vanadium.vlog.levels.INFO,
- appName: 'Google Travel'
+ appName: 'Travel Planner'
};
-
+
var async = $.Deferred();
-
+
vanadium.init(config, function(err, runtime) {
- if (err)
+ if (err) {
async.reject(err);
- else
+ } else {
async.resolve(new VanadiumWrapper(runtime));
+ }
});
-
+
return async.promise();
}
};
diff --git a/test/components/maps.js b/test/components/maps.js
index e578bf0..013661b 100644
--- a/test/components/maps.js
+++ b/test/components/maps.js
@@ -1,7 +1,6 @@
var test = require('tape');
var $ = require('../../src/util/jquery');
-var defineClass = require('../../src/util/define-class');
var Maps = require('../../src/components/maps');
var message = require ('../../src/components/message');
@@ -10,18 +9,18 @@
test('message display', function(t) {
var maps = new Maps(mockMaps);
-
+
var $messages = $('.messages', maps.$);
t.ok($messages.length, 'message display exists');
t.equals($messages.children().length, 0, 'message display is empty');
-
+
maps.message(message.info('Test message.'));
-
+
var $messageItem = $messages.children();
t.equals($messageItem.length, 1, 'message display shows 1 message');
t.equals($messageItem.text(), 'Test message.',
'message displays message text');
-
+
t.end();
});
diff --git a/test/components/message.js b/test/components/message.js
index 3105f1b..16f64af 100644
--- a/test/components/message.js
+++ b/test/components/message.js
@@ -1,5 +1,4 @@
var test = require('tape');
-var $ = require('../../src/util/jquery');
var message = require('../../src/components/message');
@@ -14,13 +13,13 @@
t.equal(msg.$[0].tagName, 'LI', 'tag name');
t.assert(msg.$.hasClass('info'), 'class info');
t.equal(msg.$.text(), 'Hello, world!', 'text');
-
+
msg.setType(message.ERROR);
t.notOk(msg.$.hasClass('info'), 'class not info');
t.assert(msg.$.hasClass('error'), 'class error');
-
+
msg.setText('hi');
t.equal(msg.$.text(), 'hi', 'text update');
-
+
t.end();
});
\ No newline at end of file
diff --git a/test/identity.js b/test/identity.js
index adb82de..814a810 100644
--- a/test/identity.js
+++ b/test/identity.js
@@ -1,5 +1,3 @@
-'use strict';
-
var test = require('tape');
var Identity = require('../src/identity');
@@ -45,7 +43,7 @@
t.equals(i.username, 'joeuser@google.com',
'should extract a username from a dev.v.io account name');
var expectedPrefix = 'joeuser@google.com/desktop_';
- t.assert(i.entityName.slice(0, expectedPrefix.length) == expectedPrefix,
+ t.assert(i.entityName.slice(0, expectedPrefix.length) === expectedPrefix,
'entityName starts with expected prefix');
t.assert(i.entityName.length > expectedPrefix.length,
'entityName is longer than expected prefix');
diff --git a/test/travel.js b/test/travel.js
index 88eae30..a341a02 100644
--- a/test/travel.js
+++ b/test/travel.js
@@ -11,9 +11,11 @@
}
test('init', function(t) {
+ /* jshint -W031 */ //instantiation smoke test
new Travel({
maps: mockMaps
});
+ /* jshint +W031 */
t.end();
cleanDom();
});
@@ -23,18 +25,18 @@
vanadiumWrapper: mockVanadiumWrapper,
maps: mockMaps
});
-
+
var $messages = $('.messages');
t.ok($messages.length, 'message display exists');
t.equals($messages.children().length, 0, 'message display is empty');
-
+
travel.info('Test message.');
-
+
var $messageItem = $messages.children();
t.equals($messageItem.length, 1, 'message display shows 1 message');
t.equals($messageItem.text(), 'Test message.',
'message displays message text');
-
+
t.end();
cleanDom();
});
@@ -43,15 +45,17 @@
var $root = $('<div>');
var root = $root[0];
$('body').append($root);
-
+
+ /* jshint -W031 */ //top-level application
new Travel({
maps: mockMaps,
vanadiumWrapper: mockVanadiumWrapper,
domRoot: root
});
-
+ /* jshint +W031 */
+
t.ok($root.children().length, 'app parented to given root');
-
+
t.end();
cleanDom();
});
\ No newline at end of file
diff --git a/test/util/define-class.js b/test/util/define-class.js
index 743d84c..68c6382 100644
--- a/test/util/define-class.js
+++ b/test/util/define-class.js
@@ -30,11 +30,11 @@
constants: ['greeting'],
events: ['stringQueried', {stringQueriedOnce: 'once'}]
});
-
+
var testInstance = new TestClass('world');
-
+
t.ok(testInstance, 'instance instantiated');
-
+
var queried = 0, queriedOnce = 0;
testInstance.stringQueried.add(function(value) {
t.equal(value, 'world', 'event argument');
@@ -44,26 +44,26 @@
t.equal(value, 'world', 'event argument');
queriedOnce++;
});
-
+
t.notOk(testInstance.stringQueried.fired(), 'event not fired');
-
+
t.equal(testInstance.greeting, 'Hello', 'public constant accessible');
t.equal(testInstance.toString(), 'Hello, world!', 'public member accessible');
-
+
t.assert(testInstance.stringQueried.fired(), 'event fired');
-
+
t.equal(queried, 1, 'event fired');
t.equal(queriedOnce, 1, 'once event fired');
-
+
testInstance.toString();
t.equal(queried, 2, 'event fired again');
t.equal(queriedOnce, 1, 'once event not fired again');
-
+
t.notOk(testInstance.getMessage, 'private member not accessible');
t.notOk(testInstance.value, 'instance field not accessible');
t.notOk(testInstance.stringQueried.fire, 'event fire not accessible');
t.notOk(testInstance.stringQueried.fireWith, 'event fireWith not accessible');
-
+
t.end();
});
@@ -71,7 +71,7 @@
var TestClass = defineClass({
init: function() {
this.privateFires = this.publicFires = 0;
-
+
var self = this;
this.privateEvent.add(function() {
self.privateFires++;
@@ -80,16 +80,16 @@
self.publicFires++;
});
},
-
+
publics: {
getPrivateFires: function() {
return this.privateFires;
},
-
+
getPublicFires: function() {
return this.publicFires;
},
-
+
trigger: function() {
this.triggerOnce.fire();
this.privateEvent();
@@ -102,27 +102,27 @@
publicEvent: 'public'
}
});
-
+
var testInstance = new TestClass();
var count = 0;
testInstance.triggerOnce.add(function() {
count++;
});
-
+
testInstance.trigger();
t.equal(count, 1, 'event fired');
testInstance.trigger();
t.equal(count, 1, 'event not fired again');
-
+
t.notOk(testInstance.privateEvent, 'private event not accessible');
t.equal(testInstance.getPrivateFires(), 2, 'private event fired twice');
t.equal(testInstance.getPublicFires(), 2, 'public event fired twice');
-
+
t.notOk($.isFunction(testInstance.triggerOnce), 'normal event not callable');
t.ok($.isFunction(testInstance.publicEvent), 'public event callable');
testInstance.publicEvent();
t.equal(testInstance.getPublicFires(), 3, 'public event fired thrice');
-
+
t.end();
});
@@ -133,18 +133,56 @@
return this.CONSTANT;
}
},
-
+
statics: {
CONSTANT: 42
}
});
-
+
t.equal(TestClass.CONSTANT, 42, 'public static access');
-
+
var testInstance = new TestClass();
-
+
t.equal(testInstance.CONSTANT, 42, 'public access');
t.equal(testInstance.getValue(), 42, 'private access');
-
+
+ t.end();
+});
+
+test('inner class', function(t) {
+ var Outer = defineClass({
+ init: function(prefix) {
+ this.prefix = prefix;
+ },
+
+ publics: {
+ inner: defineClass.innerClass({
+ init: function(suffix) {
+ this.suffix = suffix;
+ },
+
+ publics: {
+ getString: function() {
+ return this.outer.prefix + this.suffix;
+ }
+ }
+ })
+ }
+ });
+
+ var outer1 = new Outer('Hello, ');
+ var inner1x1 = outer1.inner('world!'),
+ inner1x2 = outer1.inner('Cleveland!');
+
+ var outer2 = new Outer('Goodnight, ');
+ var inner2 = outer2.inner('moon.');
+
+ t.equal(inner1x1.getString(), 'Hello, world!',
+ 'fields of outer and inner classes not corrupted by other instances');
+ t.equal(inner1x2.getString(), 'Hello, Cleveland!',
+ 'multiple instances of an inner class');
+ t.equal(inner2.getString(), 'Goodnight, moon.',
+ 'multiple instances of an outer class');
+
t.end();
});
diff --git a/test/vanadium-wrapper.js b/test/vanadium-wrapper.js
index 43d1427..09ad34d 100644
--- a/test/vanadium-wrapper.js
+++ b/test/vanadium-wrapper.js
@@ -1,8 +1,5 @@
var test = require('tape');
-var $ = require('../src/util/jquery');
-var defineClass = require('../src/util/define-class');
-
var vanadiumWrapper = require('../src/vanadium-wrapper');
var vanadiumMocks = require('../mocks/vanadium');
@@ -12,11 +9,11 @@
function setUpCrashTest(t) {
var mockVanadium = new MockVanadium(t);
var mockRuntime = new MockRuntime();
-
+
var context = {
bindCrashHandler: function(err) {
var self = this;
- self.vanadiumWrapper.crash.add(function(err) {
+ self.vanadiumWrapper.onCrash.add(function(err) {
self.crashErr = err;
});
},
@@ -24,7 +21,7 @@
mockRuntime.fireCrash(err);
}
};
-
+
vanadiumWrapper.init(mockVanadium).then(
function(v) {
context.vanadiumWrapper = v;
@@ -32,19 +29,19 @@
function(err) {
t.fail('init error');
});
-
+
mockVanadium.finishInit(null, mockRuntime);
-
+
return context;
}
test('crashBefore', function(t) {
var crashTest = setUpCrashTest(t);
-
+
crashTest.crash('I lost the game.');
crashTest.bindCrashHandler();
t.equal(crashTest.crashErr, 'I lost the game.');
-
+
t.end();
});
@@ -52,9 +49,9 @@
var crashTest = setUpCrashTest(t);
crashTest.bindCrashHandler();
t.notOk(crashTest.crashErr, 'no crash yet');
-
+
crashTest.crash('I lost the game.');
t.equal(crashTest.crashErr, 'I lost the game.');
-
+
t.end();
});
\ No newline at end of file