convert to commonjs, add point in polygon + line intersection functions

This commit is contained in:
Max Ogden 2010-12-10 23:59:28 -08:00
parent 665458841e
commit 77201deba9
9 changed files with 132 additions and 2976 deletions

View File

@ -1,41 +1,57 @@
h1. GeoJSON Utilities for JavaScript
Here you will find some simple utility functions to help you manipulate and work with GeoJSON objects.
Here you will find some simple utility functions to help you manipulate and work with GeoJSON objects.
h2. Feature Collection Wrapping
Some algorithms ported from https://github.com/bjwbell/canvas-geolib
This was written because I wanted to display arbitrary GeoJSON objects using OpenLayers, but an OpenLayers Strategy will only accept a FeatureCollection. I use these functions to turn my basic geometries (Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon) into valid FeatureCollections.
h2. Line intersections
Here's an array of two GeoJSON Point objects:
<pre>
<code>
var g = require('geojson-utils.js').init()
<pre><code>
[{"coordinates" : [-122.672727,45.521561],"type" : "Point"},
{"coordinates" : [-122.675647,45.523729],"type" : "Point"}]
</code></pre>
g.linesIntersect([[0,6],[6,6]], [[0,3],[6,3]])
=> false
If you feed that through the @GeoJSON.feature_collection_for(geometry)@ function you will receive:
g.linesIntersect([[0,6],[6,6]], [[3,0],[3,9]])
=> [3,6]
</code>
</pre>
<pre><code>
{ "type" : "FeatureCollection",
"features" : [{"geometry" :
{ "type" : "GeometryCollection",
"geometries" : [
{ "coordinates" : [-122.672727,45.521561], "type" : "Point" },
{ "coordinates" : [-122.675647,45.523729], "type" : "Point" }
]
}}
]
}
</code></pre>
h2. Point in polygon
h2. Polygon Intersections
<pre>
<code>
g.pointInPolygon([3,3], [[0,0],[0,6],[6,6],[6,0]])
=> true
g.pointInPolygon([-1,-1], [[0,0],[0,6],[6,6],[6,0]])
=> false
</code>
</pre>
Given some GeoJSON polygon objects:
h2. and much much more! read the codez
<pre><code>
var intersects_1 = { "type" :"Polygon","coordinates": [[0, 5], [5, 5], [3, 9]]};
var intersects_2 = { "type" :"Polygon","coordinates": [[9, 0], [3, 3], [9, 9]]};
</code></pre>
h4. License
If you feed these through @GeoJSON.polygonsIntersect(intersects_1, intersects_2)@ function you will receive @true@. If your polygons don't intercept, you will receive @false@.
The MIT License
Copyright (c) 2010 Max Ogden
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

88
geojson-utils.js Normal file
View File

@ -0,0 +1,88 @@
exports.init = function() {
var g = {};
g.scalarMultiply = function(xy, scalar) {
return [xy[0] * scalar, xy[1] * scalar];
}
g.subtractVector = function(xy, vector) {
return g.add(xy, g.scalarMultiply(vector, -1));
}
g.addVector = function(xy, vector) {
return [xy[0] + vector[0], xy[1] + vector[1]];
}
g.crossProduct = function(v1, v2) {
return v1[0] * v2[1] - v2[0] * v1[1];
}
g.linesIntersect = function(seg1, seg2) {
var p = seg1[0],
r = g.subtractVector(seg1[1], p),
q = seg2[0],
s = g.subtractVector(seg2[1], q);
var rCrossS = g.crossProduct(r, s);
var t = g.cross(g.subtractVector(q,p), s) / rCrossS;
var u = g.cross(g.subtractVector(q,p), r) / rCrossS;
if (0 <= u && u <= 1 && 0 <= t && t <= 1) {
var intersectionPoint = g.addVector(p, g.scalarMultiply(r, t));
return intersectionPoint;
} else {
return false;
}
}
g.pointInPolygon = function(pt, poly) {
for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1] < poly[i][1]))
&& (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])
&& (c = !c);
return c;
}
g.numberToRadius = function(number) {
return number * Math.PI / 180;
}
g.numberToDegree = function(number) {
return number * 180 / Math.PI;
}
g.drawCircle = function(radius, center) {
// convert degree/km to radiant
var dist = radius / 6371;
var radCenter = [g.numberToRadius(center[0]), g.numberToRadius(center[1])];
// 15 sided circle; the larger the radius the more inaccurate it will be
var steps = 15;
var poly = [[center[0], center[1]]];
for (var i = 0; i < steps; i++) {
var brng = 2 * Math.PI * i / steps;
var lat = Math.asin(Math.sin(radCenter[0]) * Math.cos(dist) +
Math.cos(radCenter[0]) * Math.sin(dist) * Math.cos(brng));
var lng = radCenter[1] + Math.atan2(Math.sin(brng) * Math.sin(dist) *
Math.cos(radCenter[0]),
Math.cos(dist) - Math.sin(radCenter[0]) *
Math.sin(lat));
poly[i] = [];
poly[i][0] = g.numberToDegree(lat);
poly[i][1] = g.numberToDegree(lng);
}
return poly;
}
g.rectangleCentroid = function(bbox) {
var xmin = bbox[0], ymin = bbox[1], xmax = bbox[2], ymax = bbox[3];
var xwidth = xmax - xmin;
var ywidth = ymax - ymin;
return [xmin + xwidth/2, ymin + ywidth/2];
}
g.metersToDegrees = function(meters) {
//non spherical; the larger the area the more innaccurate it will be
return meters/111319.9;
}
return g;
}

View File

@ -1,30 +0,0 @@
var GeoJSON = function() {
return {
// map borrowed from http://github.com/janl/mustache.js/blob/master/mustache.js
map : function(array, fn) {
if (typeof array.map == "function") {
return array.map(fn);
} else {
var r = [];
var l = array.length;
for(var i = 0; i < l; i++) {
r.push(fn(array[i]));
}
return r;
}
},
collect_geometries : function(geometries) {
if (geometries.type == 'GeometryCollection')
return geometries;
return [{"type" : "GeometryCollection", "geometries" : geometries }]
},
collect_features : function(features){
if (features.type == 'FeatureCollection')
return features;
return { "type" : "FeatureCollection", "features" : GeoJSON.map(features, function(feature){return {"geometry" : feature}})}
},
feature_collection_for : function(geojson) {
return this.collect_features(this.collect_geometries(geojson));
}
};
}();

View File

@ -1,114 +0,0 @@
var GeoJSON = function() {
return {
Vector: function(x, y) {
this.x = x;
this.y = y;
this.scalarMult = function(scalar) {
return new GeoJSON.Vector(this.x * scalar, this.y * scalar);
}
this.dot = function(v2) {
return this.x * v2.x + this.y * v2.y;
};
this.perp = function() {
return new GeoJSON.Vector(-1 * this.y, this.x);
};
this.subtract = function(v2) {
return this.add(v2.scalarMult(-1));
};
this.add = function(v2) {
return new GeoJSON.Vector(this.x + v2.x, this.y + v2.y);
}
},
Segment: function(p1, p2) {
this.p1 = p1;
this.p2 = p2;
},
cross: function(v1, v2) {
return v1.x * v2.y - v2.x * v1.y;
},
epsilon: 10e-6,
doesnt_intersect: 0,
parallel_doesnt_intersect: 1,
colinear_doesnt_intersect: 2,
intersect: 3,
colinear_intersect: 4,
linesIntersect: function(seg1, seg2, intersectionPoint) {
p = seg1.p1;
r = seg1.p2.subtract(seg1.p1);
q = seg2.p1;
s = seg2.p2.subtract(seg2.p1);
rCrossS = GeoJSON.cross(r, s);
if(rCrossS <= GeoJSON.epsilon && rCrossS >= -1 * GeoJSON.epsilon){
return GeoJSON.parallel_doesnt_intersect;
}
t = GeoJSON.cross(q.subtract(p), s)/rCrossS;
u = GeoJSON.cross(q.subtract(p), r)/rCrossS;
if(0 <= u && u <= 1 && 0 <= t && t <= 1){
intPoint = p.add(r.scalarMult(t));
intersectionPoint.x = intPoint.x;
intersectionPoint.y = intPoint.y;
return GeoJSON.intersect;
}else{
return GeoJSON.doesnt_intersect;
}
},
polygonsIntersect: function(pol1, pol2) {
//todo: check for large distances
//todo: make this not suck, written after not sleeping for 24 hours
for(var x = 0; x < pol1.coordinates.length; x++) {
for(var y = 0; y < pol2.coordinates.length; y++) {
if (y === pol2.coordinates.length-1 && x !== pol1.coordinates.length-1) {
var xa1 = pol1.coordinates[x][0];
var ya1 = pol1.coordinates[x][1];
var xa2 = pol1.coordinates[x+1][0];
var ya2 = pol1.coordinates[x+1][1];
var xb1 = pol2.coordinates[y][0];
var yb1 = pol2.coordinates[y][1];
var xb2 = pol2.coordinates[0][0];
var yb2 = pol2.coordinates[0][1];
} else if (x === pol1.coordinates.length-1 && y !== pol2.coordinates.length-1){
var xa1 = pol1.coordinates[x][0];
var ya1 = pol1.coordinates[x][1];
var xa2 = pol1.coordinates[0][0];
var ya2 = pol1.coordinates[0][1];
var xb1 = pol2.coordinates[y][0];
var yb1 = pol2.coordinates[y][1];
var xb2 = pol2.coordinates[y+1][0];
var yb2 = pol2.coordinates[y+1][1];
} else if (x === pol1.coordinates.length-1 && y === pol2.coordinates.length-1){
var xa1 = pol1.coordinates[x][0];
var ya1 = pol1.coordinates[x][1];
var xa2 = pol1.coordinates[0][0];
var ya2 = pol1.coordinates[0][1];
var xb1 = pol2.coordinates[y][0];
var yb1 = pol2.coordinates[y][1];
var xb2 = pol2.coordinates[0][0];
var yb2 = pol2.coordinates[0][1];
} else {
var xa1 = pol1.coordinates[x][0];
var ya1 = pol1.coordinates[x][1];
var xa2 = pol1.coordinates[x+1][0];
var ya2 = pol1.coordinates[x+1][1];
var xb1 = pol2.coordinates[y][0];
var yb1 = pol2.coordinates[y][1];
var xb2 = pol2.coordinates[y+1][0];
var yb2 = pol2.coordinates[y+1][1];
}
var seg1 = new GeoJSON.Segment(new GeoJSON.Vector(xa1, ya1), new GeoJSON.Vector(xa2, ya2));
var seg2 = new GeoJSON.Segment(new GeoJSON.Vector(xb1, yb1), new GeoJSON.Vector(xb2, yb2));
var intersectionPoint = new GeoJSON.Vector(0, 0);
if(GeoJSON.linesIntersect(seg1, seg2, intersectionPoint) == GeoJSON.intersect) {
return true;
}
}
}
return false;
}
}
}();

View File

@ -1,26 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Jasmine Test Runner</title>
<link rel="stylesheet" type="text/css" href="jasmine-0.11.1/jasmine.css">
<script type="text/javascript" src="jasmine-0.11.1/jasmine.js"></script>
<script type="text/javascript" src="jasmine-0.11.1/jasmine-html.js"></script>
<!-- include source files here... -->
<script type="text/javascript" src="../intersections.js"></script>
<script type="text/javascript" src="../helpers.js"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="geojson_utilities_spec.js"></script>
</head>
<body>
<script type="text/javascript">
jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
jasmine.getEnv().execute();
</script>
</body>
</html>

View File

@ -1,87 +0,0 @@
describe("GeoJSON intersection functions", function() {
describe("#polygonsIntersect", function() {
it("should detect polygons that intersect", function() {
var intersecting1 = {"type":"Polygon","coordinates":[[-122.34100341796875,47.739446498637776],[-122.5030517578125,47.60258275608435],[-122.39593505859375,47.492276537740416],[-122.1844482421875,47.51732435953473],[-122.23663330078125,47.72097237165927],[-122.34100341796875,47.739446498637776]]};
var intersecting2 = {"type":"Polygon","coordinates":[[-122.23388671875,47.80036462595262],[-122.3162841796875,47.688626879942966],[-122.14187622070312,47.576649235558236],[-122.04437255859375,47.75422108200102],[-122.091064453125,47.782834903292276],[-122.23388671875,47.80036462595262]]};
var not_intersecting = {"type":"Polygon","coordinates":[[-122.48931884765625,47.86950316614039],[-122.80517578125,47.655336290758285],[-122.63763427734375,47.60258275608435],[-122.35061645507812,47.86766066723359],[-122.48931884765625,47.86950316614039]]};
expect(GeoJSON.polygonsIntersect(intersecting1, intersecting2)).toBeTruthy();
expect(GeoJSON.polygonsIntersect(intersecting1, not_intersecting)).toBeFalsy();
});
});
});
describe("GeoJSON helper functions", function() {
describe("#collect_geometries", function() {
it("should wrap geometries in a geometry collection", function() {
var points = [{"coordinates" : [-122.672727,45.521561], "type" : "Point"},
{"coordinates" : [-122.675647,45.523729], "type" : "Point"}];
var geometry_collection = [{"type" : "GeometryCollection", "geometries" : points }];
expect(GeoJSON.collect_geometries(points)).toEqual(geometry_collection);
});
it("shouldn't alter existing geometry collections", function() {
var geometry_collection = { "type" : "GeometryCollection",
"geometries" : [
{ "coordinates" : [-122.672727,45.521561], "type" : "Point" },
{ "coordinates" : [-122.675647,45.523729], "type" : "Point" }
]
}
expect(GeoJSON.collect_geometries(geometry_collection)).toEqual(geometry_collection);
});
});
describe("#collect_features", function() {
it("should wrap geometry collections in a feature collection", function() {
var geometry_collections = [{"type" : "GeometryCollection",
"geometries" :
[{"type":"LineString",
"coordinates" : [[-122.593225, 45.563783], [-122.592189, 45.563325]]
}]
},
{"type" : "GeometryCollection",
"geometries" :
[{"type":"LineString",
"coordinates" : [[-122.593225, 45.563783], [-122.592189, 45.563325]]
}]
}]
var feature_collection = {"type" : "FeatureCollection",
"features" : [{"geometry" : geometry_collections[0]}, {"geometry" : geometry_collections[1]}]
}
expect(GeoJSON.collect_features(geometry_collections)).toEqual(feature_collection);
});
it("shouldn't alter existing feature collections", function() {
feature = { "type" : "FeatureCollection",
"features" : [{"geometry" :
{ "type" : "GeometryCollection",
"geometries" : [
{ "coordinates" : [-122.672727,45.521561], "type" : "Point" },
{ "coordinates" : [-122.675647,45.523729], "type" : "Point" }
]
}}
]
}
expect(GeoJSON.collect_features(feature)).toEqual(feature);
});
});
describe("#feature_collection_for", function() {
it("should convert arbitrary arrays of individual GeoJSON objects into a feature collection", function() {
var points = [{"coordinates" : [-122.672727,45.521561],"type" : "Point"},
{"coordinates" : [-122.675647,45.523729],"type" : "Point"}]
expect(GeoJSON.feature_collection_for(points)).toEqual(
{ "type" : "FeatureCollection",
"features" : [{"geometry" :
{ "type" : "GeometryCollection",
"geometries" : [
{ "coordinates" : [-122.672727,45.521561], "type" : "Point" },
{ "coordinates" : [-122.675647,45.523729], "type" : "Point" }
]
}}
]
}
);
});
});
});

View File

@ -1,182 +0,0 @@
jasmine.TrivialReporter = function(doc) {
this.document = doc || document;
this.suiteDivs = {};
this.logRunningSpecs = false;
};
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
var el = document.createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(document.createTextNode(child));
} else {
if (child) { el.appendChild(child); }
}
}
for (var attr in attrs) {
if (attr == "className") {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
};
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
var showPassed, showSkipped;
this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
this.createDom('div', { className: 'banner' },
this.createDom('div', { className: 'logo' },
"Jasmine",
this.createDom('span', { className: 'version' }, runner.env.versionString())),
this.createDom('div', { className: 'options' },
"Show ",
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
)
),
this.runnerDiv = this.createDom('div', { className: 'runner running' },
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
);
this.document.body.appendChild(this.outerDiv);
var suites = runner.suites();
for (var i = 0; i < suites.length; i++) {
var suite = suites[i];
var suiteDiv = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
this.suiteDivs[suite.id] = suiteDiv;
var parentDiv = this.outerDiv;
if (suite.parentSuite) {
parentDiv = this.suiteDivs[suite.parentSuite.id];
}
parentDiv.appendChild(suiteDiv);
}
this.startedAt = new Date();
var self = this;
showPassed.onchange = function(evt) {
if (evt.target.checked) {
self.outerDiv.className += ' show-passed';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
}
};
showSkipped.onchange = function(evt) {
if (evt.target.checked) {
self.outerDiv.className += ' show-skipped';
} else {
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
}
};
};
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
var results = runner.results();
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
this.runnerDiv.setAttribute("class", className);
//do it twice for IE
this.runnerDiv.setAttribute("className", className);
var specs = runner.specs();
var specCount = 0;
for (var i = 0; i < specs.length; i++) {
if (this.specFilter(specs[i])) {
specCount++;
}
}
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
};
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
var results = suite.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.totalCount == 0) { // todo: change this to check results.skipped
status = 'skipped';
}
this.suiteDivs[suite.id].className += " " + status;
};
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
if (this.logRunningSpecs) {
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
var results = spec.results();
var status = results.passed() ? 'passed' : 'failed';
if (results.skipped) {
status = 'skipped';
}
var specDiv = this.createDom('div', { className: 'spec ' + status },
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
this.createDom('a', {
className: 'description',
href: '?spec=' + encodeURIComponent(spec.getFullName()),
title: spec.getFullName()
}, spec.description));
var resultItems = results.getItems();
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {
var result = resultItems[i];
if (result.type == 'log') {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
} else if (result.type == 'expect' && result.passed && !result.passed()) {
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
if (result.trace.stack) {
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
}
}
}
if (messagesDiv.childNodes.length > 0) {
specDiv.appendChild(messagesDiv);
}
this.suiteDivs[spec.suite.id].appendChild(specDiv);
};
jasmine.TrivialReporter.prototype.log = function() {
var console = jasmine.getGlobal().console;
if (console && console.log) console.log.apply(console, arguments);
};
jasmine.TrivialReporter.prototype.getLocation = function() {
return this.document.location;
};
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
var paramMap = {};
var params = this.getLocation().search.substring(1).split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
}
if (!paramMap["spec"]) return true;
return spec.getFullName().indexOf(paramMap["spec"]) == 0;
};

View File

@ -1,166 +0,0 @@
body {
font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
}
.jasmine_reporter a:visited, .jasmine_reporter a {
color: #303;
}
.jasmine_reporter a:hover, .jasmine_reporter a:active {
color: blue;
}
.run_spec {
float:right;
padding-right: 5px;
font-size: .8em;
text-decoration: none;
}
.jasmine_reporter {
margin: 0 5px;
}
.banner {
color: #303;
background-color: #fef;
padding: 5px;
}
.logo {
float: left;
font-size: 1.1em;
padding-left: 5px;
}
.logo .version {
font-size: .6em;
padding-left: 1em;
}
.runner.running {
background-color: yellow;
}
.options {
text-align: right;
font-size: .8em;
}
.suite {
border: 1px outset gray;
margin: 5px 0;
padding-left: 1em;
}
.suite .suite {
margin: 5px;
}
.suite.passed {
background-color: #dfd;
}
.suite.failed {
background-color: #fdd;
}
.spec {
margin: 5px;
padding-left: 1em;
clear: both;
}
.spec.failed, .spec.passed, .spec.skipped {
padding-bottom: 5px;
border: 1px solid gray;
}
.spec.failed {
background-color: #fbb;
border-color: red;
}
.spec.passed {
background-color: #bfb;
border-color: green;
}
.spec.skipped {
background-color: #bbb;
}
.messages {
border-left: 1px dashed gray;
padding-left: 1em;
padding-right: 1em;
}
.passed {
background-color: #cfc;
display: none;
}
.failed {
background-color: #fbb;
}
.skipped {
color: #777;
background-color: #eee;
display: none;
}
/*.resultMessage {*/
/*white-space: pre;*/
/*}*/
.resultMessage span.result {
display: block;
line-height: 2em;
color: black;
}
.resultMessage .mismatch {
color: black;
}
.stackTrace {
white-space: pre;
font-size: .8em;
margin-left: 10px;
max-height: 5em;
overflow: auto;
border: 1px inset red;
padding: 1em;
background: #eef;
}
.finished-at {
padding-left: 1em;
font-size: .6em;
}
.show-passed .passed,
.show-skipped .skipped {
display: block;
}
#jasmine_content {
position:fixed;
right: 100%;
}
.runner {
border: 1px solid gray;
display: block;
margin: 5px 0;
padding: 2px 0 2px 10px;
}

File diff suppressed because it is too large Load Diff