Merge branch 'master' into gh-pages

This commit is contained in:
Rufus Pollock 2014-01-12 22:47:00 +00:00
commit d4a0ea6093
68 changed files with 21596 additions and 16091 deletions

4
.travis.yml Normal file
View File

@ -0,0 +1,4 @@
language: node_js
node_js:
- "0.10"
script: phantomjs test/qunit/runner.js test/index.html

View File

@ -1,4 +1,4 @@
Copyright (c) 2011 Max Ogden & Contributors
Copyright (c) 2011-2014 Max Ogden, Rufus Pollock and Contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@ -1,3 +1,5 @@
[![Build Status](https://travis-ci.org/okfn/recline.png)](https://travis-ci.org/okfn/recline)
A simple but powerful library for building data applications in pure Javascript and HTML.
<h3><a href="http://okfnlabs.org/recline/">Recline Website - including Overview, Documentation, Demos etc</a></h3>
@ -35,12 +37,15 @@ See CONTRIBUTING.md.
Possible breaking changes
* Many backends moved to their own repositories #314
* Updated Leaflet to latest version 0.4.4 #220
* Added marker clustering in map view to handle a large number of markers
* Dataset.restore method removed (not used internally except from Multiview.restore)
* Views no longer call render in initialize but must be called client code
* Backend.Memory.Store attribute for holding 'records' renamed to `records` from `data`
* Require new underscore.deferred vendor library for all use (jQuery no longer required if just using recline.dataset.js)
* View.el is now the raw DOM element. If you want a jQuery-wrapped version, use view.$el. #350
* Upgraded timelinejs lib - #316
### v0.5 - July 5th 2012 (first public release)

View File

@ -2,7 +2,7 @@
<li><a href="https://github.com/okfn/recline.backend.gdocs">gdocs: Google Docs (Spreadsheet)</a></li>
<li><a href="{{page.root}}/docs/src/backend.csv.html">csv: CSV files</a></li>
<li><a href="https://github.com/okfn/recline.backend.solr">solr: SOLR</a> (partial)</li>
<li><a href="{{page.root}}/docs/src/backend.elasticsearch.html">elasticsearch: ElasticSearch</a></li>
<li><a href="https://github.com/okfn/elasticsearch.js">elasticsearch: ElasticSearch</a></li>
<li><a href="{{page.root}}/docs/src/backend.dataproxy.html">dataproxy: DataProxy (CSV and XLS on the Web)</a></li>
<li><a href="https://github.com/okfn/recline.backend.ckan">ckan: CKAN</a> &ndash; support for <a href="http://docs.ckan.org/en/latest/datastore.html">CKAN datastore</a></li>
<li><a href="https://github.com/okfn/recline.backend.couchdb">couchdb: CouchDB</a></li>

View File

@ -1,13 +0,0 @@
var dataset = new recline.Model.Dataset({
url: 'http://datahub.io/dataset/rendition-on-record/ac5a28ea-eb52-4b0a-a399-5dcc1becf9d9/api',
backend: 'elasticsearch'
});
dataset.fetch();
// For demonstrations purposes display the data in a grid
var grid = new recline.View.SlickGrid({
model: dataset
});
$('#my-elasticsearch').append(grid.el);

View File

@ -8,7 +8,7 @@
<link rel="stylesheet" href="{{page.root}}vendor/leaflet.markercluster/MarkerCluster.Default.ie.css" />
<![endif]-->
<link rel="stylesheet" href="{{page.root}}vendor/slickgrid/2.0.1/slick.grid.css">
<link rel="stylesheet" href="{{page.root}}vendor/timeline/20120520/css/timeline.css">
<link rel="stylesheet" href="{{page.root}}vendor/timeline/css/timeline.css">
<!-- Recline CSS components -->
<link rel="stylesheet" href="{{page.root}}css/grid.css">
@ -21,21 +21,22 @@
<!-- 3rd party JS libraries -->
<script type="text/javascript" src="{{page.root}}vendor/jquery/1.7.1/jquery.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/underscore/1.4.2/underscore.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/backbone/0.9.2/backbone.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/underscore/1.4.4/underscore.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/backbone/1.0.0/backbone.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/mustache/0.5.0-dev/mustache.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/bootstrap/2.0.2/bootstrap.js"></script>
<!--[if lte IE 8]>
<script language="javascript" type="text/javascript" src="{{page.root}}vendor/flot/excanvas.min.js"></script>
<![endif]-->
<script type="text/javascript" src="{{page.root}}vendor/flot/jquery.flot.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/flot/jquery.flot.time.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/leaflet/0.4.4/leaflet.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/leaflet.markercluster/leaflet.markercluster.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/jquery-ui-1.8.16.custom.min.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/jquery.event.drag-2.0.min.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/slickgrid/2.0.1/slick.grid.min.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/moment/1.6.2/moment.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/timeline/20120520/js/timeline.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/moment/2.0.0/moment.js"></script>
<script type="text/javascript" src="{{page.root}}vendor/timeline/js/timeline.js"></script>
<!--[if lte IE 7]>
<script language="javascript" type="text/javascript" src="{{page.root}}vendor/json/json2.js"></script>
<![endif]-->
@ -52,8 +53,7 @@
<script type="text/javascript" src="{{page.root}}src/model.js"></script>
<script type="text/javascript" src="{{page.root}}src/backend.memory.js"></script>
<script type="text/javascript" src="{{page.root}}src/backend.dataproxy.js"></script>
<script type="text/javascript" src="{{page.root}}src/backend.gdocs.js"></script>
<script type="text/javascript" src="{{page.root}}src/backend.elasticsearch.js"></script>
<script type="text/javascript" src="http://okfnlabs.org/recline.backend.gdocs/backend.gdocs.js"></script>
<script type="text/javascript" src="{{page.root}}src/backend.csv.js"></script>
<!-- views -->

View File

@ -73,7 +73,18 @@ var createExplorer = function(dataset, state) {
id: 'grid',
label: 'Grid',
view: new recline.View.SlickGrid({
model: dataset
model: dataset,
state: {
gridOptions: {
editable: true,
enabledAddRow: true,
enableCellNavigation: true
},
columnsEditor: [
{ column: 'date', editor: Slick.Editors.Date },
{ column: 'title', editor: Slick.Editors.Text }
]
}
})
},
{
@ -81,6 +92,7 @@ var createExplorer = function(dataset, state) {
label: 'Graph',
view: new recline.View.Graph({
model: dataset
})
},
{

View File

@ -3,9 +3,10 @@ this.recline = this.recline || {};
this.recline.Model = this.recline.Model || {};
(function(my) {
"use strict";
// use either jQuery or Underscore Deferred depending on what is available
var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
var Deferred = (typeof jQuery !== "undefined" && jQuery.Deferred) || _.Deferred;
// ## <a id="dataset">Dataset</a>
my.Dataset = Backbone.Model.extend({
@ -15,6 +16,7 @@ my.Dataset = Backbone.Model.extend({
// ### initialize
initialize: function() {
var self = this;
_.bindAll(this, 'query');
this.backend = null;
if (this.get('backend')) {
@ -34,8 +36,9 @@ my.Dataset = Backbone.Model.extend({
this.facets = new my.FacetList();
this.recordCount = null;
this.queryState = new my.Query();
this.queryState.bind('change', this.query);
this.queryState.bind('facet:add', this.query);
this.queryState.bind('change facet:add', function () {
self.query(); // We want to call query() without any arguments.
});
// store is what we query and save against
// store will either be the backend or be a memory store if Backend fetch
// tells us to use memory store
@ -68,7 +71,13 @@ my.Dataset = Backbone.Model.extend({
}
function handleResults(results) {
var out = self._normalizeRecordsAndFields(results.records, results.fields);
// if explicitly given the fields
// (e.g. var dataset = new Dataset({fields: fields, ...})
// use that field info over anything we get back by parsing the data
// (results.fields)
var fields = self.get('fields') || results.fields;
var out = self._normalizeRecordsAndFields(results.records, fields);
if (results.useMemoryStore) {
self._store = new recline.Backend.Memory.Store(out.records, out.fields);
}
@ -298,7 +307,7 @@ my.Record = Backbone.Model.extend({
//
// NB: if field is undefined a default '' value will be returned
getFieldValue: function(field) {
val = this.getFieldValueUnrendered(field);
var val = this.getFieldValueUnrendered(field);
if (field && !_.isUndefined(field.renderer)) {
val = field.renderer(val, field, this.toJSON());
}
@ -588,10 +597,11 @@ this.recline.Backend = this.recline.Backend || {};
this.recline.Backend.Memory = this.recline.Backend.Memory || {};
(function(my) {
"use strict";
my.__type__ = 'memory';
// private data - use either jQuery or Underscore Deferred depending on what is available
var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
var Deferred = (typeof jQuery !== "undefined" && jQuery.Deferred) || _.Deferred;
// ## Data Wrapper
//
@ -692,9 +702,9 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
integer: function (e) { return parseFloat(e, 10); },
'float': function (e) { return parseFloat(e, 10); },
number: function (e) { return parseFloat(e, 10); },
string : function (e) { return e.toString() },
date : function (e) { return new Date(e).valueOf() },
datetime : function (e) { return new Date(e).valueOf() }
string : function (e) { return e.toString(); },
date : function (e) { return moment(e).valueOf(); },
datetime : function (e) { return new Date(e).valueOf(); }
};
var keyedFields = {};
_.each(self.fields, function(field) {
@ -725,8 +735,8 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
}
function range(record, filter) {
var startnull = (filter.start == null || filter.start === '');
var stopnull = (filter.stop == null || filter.stop === '');
var startnull = (filter.start === null || filter.start === '');
var stopnull = (filter.stop === null || filter.stop === '');
var parse = getDataParser(filter);
var value = parse(record[filter.field]);
var start = parse(filter.start);
@ -750,8 +760,8 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
if (queryObj.q) {
var terms = queryObj.q.split(' ');
var patterns=_.map(terms, function(term) {
return new RegExp(term.toLowerCase());;
});
return new RegExp(term.toLowerCase());
});
results = _.filter(results, function(rawdoc) {
var matches = true;
_.each(patterns, function(pattern) {

1044
dist/recline.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -76,8 +76,8 @@ much more limited if you are just using a Backend. Specifically:
<script type="text/javascript" src="vendor/jquery/1.7.1/jquery.js"></script>
<script type="text/javascript" src="vendor/underscore/1.1.6/underscore.js"></script>
<script type="text/javascript" src="vendor/backbone/0.5.1/backbone.js"></script>
<!-- include the backend code you need e.g. here for gdocs -->
<script type="text/javascript" src="src/backend.gdocs.js"></script>
<!-- include the backend code you need e.g. here for csv -->
<script type="text/javascript" src="src/backend.csv.js"></script>
<!-- Or you can just include all of recline. -->
<script type="text/javascript" src="dist/recline.js"></script>
@ -99,6 +99,8 @@ a bespoke chooser and a Kartograph (svg-only) map.
</div>
{% highlight javascript %}
// include the Recline backend for Google Docs
<script type="text/javascript" src="http://okfnlabs.org/recline.backend.gdocs/backend.gdocs.js"></script>
{% include example-backends-gdocs.js %}
{% endhighlight %}
@ -106,26 +108,19 @@ a bespoke chooser and a Kartograph (svg-only) map.
<div id="my-gdocs" class="doc-ex-rendered">&nbsp;</div>
<script type="text/javascript" src="http://okfnlabs.org/recline.backend.gdocs/backend.gdocs.js">&nbsp;</script>
<script type="text/javascript">
{% include example-backends-gdocs.js %}
</script>
## Loading Data from ElasticSearch and the DataHub
## Loading Data from ElasticSearch
Recline supports ElasticSearch as a full read/write/query backend. It also means that Recline can load data from the [DataHub's](http://datahub.io/) data API as that is ElasticSearch compatible. Here's an example, using [this dataset about Rendition flights](http://datahub.io/dataset/rendition-on-record/ac5a28ea-eb52-4b0a-a399-5dcc1becf9d9') on the DataHub:
Recline supports ElasticSearch as a full read/write/query backend via the
[ElasticSearch.js library][esjs]. See the library for examples.
{% highlight javascript %}
{% include example-backends-elasticsearch.js %}
{% endhighlight %}
### Result
<div id="my-elasticsearch" class="doc-ex-rendered">&nbsp;</div>
<script type="text/javascript">
{% include example-backends-elasticsearch.js %}
</script>
[esjs]: https://github.com/okfn/elasticsearch.js
## Loading data from CSV files

View File

@ -102,3 +102,30 @@ view.featureparse = function (e) {
};
{% endhighlight %}
### Turning on clustering
You can turn on clustering of markers by setting the cluster option:
var map = new recline.View.Map({
model: dataset
state: {
cluster: true;
}
});
You could also enable marker clustering only if you have more than a
certain number of markers. Here's an example:
// var map is recline.View.Map instance
// marker cluster threshold
var threshold = 65;
// enable clustering if there is a large number of markers
var countAfter = 0;
map.features.eachLayer(function(){countAfter++;});
if (countAfter > threshold) {
// note the map will auto-redraw when you change state!
map.state.set({cluster: true});
}

View File

@ -130,6 +130,7 @@ library and the Recline Flot Graph view:
<script language="javascript" type="text/javascript" src="vendor/flot/excanvas.min.js"></script>
<![endif]-->
<script type="text/javascript" src="vendor/flot/jquery.flot.js"></script>
<script type="text/javascript" src="vendor/flot/jquery.flot.time.js"></script>
<script type="text/javascript" src="src/view.graph.js"></script>
{% endhighlight %}
@ -247,11 +248,11 @@ First, add the additional dependencies for the timeline view. The timeline is bu
{% highlight html %}
<!-- css -->
<link rel="stylesheet" href="vendor/timeline/20120520/css/timeline.css">
<link rel="stylesheet" href="vendor/timeline/css/timeline.css">
<!-- javascript -->
<script type="text/javascript" src="vendor/moment/1.6.2/moment.js"></script>
<script type="text/javascript" src="vendor/timeline/20120520/js/timeline.js"></script>
<script type="text/javascript" src="vendor/moment/2.0.0/moment.js"></script>
<script type="text/javascript" src="vendor/timeline/js/timeline.js"></script>
{% endhighlight %}
Now, create a new div for the map (must have an explicit height for the timeline to render):

View File

@ -4,10 +4,11 @@ this.recline.Backend.CSV = this.recline.Backend.CSV || {};
// Note that provision of jQuery is optional (it is **only** needed if you use fetch on a remote file)
(function(my) {
"use strict";
my.__type__ = 'csv';
// use either jQuery or Underscore Deferred depending on what is available
var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
var Deferred = (typeof jQuery !== "undefined" && jQuery.Deferred) || _.Deferred;
// ## fetch
//
@ -32,37 +33,46 @@ this.recline.Backend.CSV = this.recline.Backend.CSV || {};
var reader = new FileReader();
var encoding = dataset.encoding || 'UTF-8';
reader.onload = function(e) {
var rows = my.parseCSV(e.target.result, dataset);
dfd.resolve({
records: rows,
metadata: {
filename: dataset.file.name
},
useMemoryStore: true
});
var out = my.extractFields(my.parseCSV(e.target.result, dataset), dataset);
out.useMemoryStore = true;
out.metadata = {
filename: dataset.file.name
}
dfd.resolve(out);
};
reader.onerror = function (e) {
alert('Failed to load file. Code: ' + e.target.error.code);
};
reader.readAsText(dataset.file, encoding);
} else if (dataset.data) {
var rows = my.parseCSV(dataset.data, dataset);
dfd.resolve({
records: rows,
useMemoryStore: true
});
var out = my.extractFields(my.parseCSV(dataset.data, dataset), dataset);
out.useMemoryStore = true;
dfd.resolve(out);
} else if (dataset.url) {
jQuery.get(dataset.url).done(function(data) {
var rows = my.parseCSV(data, dataset);
dfd.resolve({
records: rows,
useMemoryStore: true
});
var out = my.extractFields(my.parseCSV(data, dataset), dataset);
out.useMemoryStore = true;
dfd.resolve(out);
});
}
return dfd.promise();
};
// Convert array of rows in { records: [ ...] , fields: [ ... ] }
// @param {Boolean} noHeaderRow If true assume that first row is not a header (i.e. list of fields but is data.
my.extractFields = function(rows, noFields) {
if (noFields.noHeaderRow !== true && rows.length > 0) {
return {
fields: rows[0],
records: rows.slice(1)
}
} else {
return {
records: rows
}
}
};
// ## parseCSV
//
// Converts a Comma Separated Values string into an array of arrays.
@ -83,6 +93,8 @@ this.recline.Backend.CSV = this.recline.Backend.CSV || {};
// fields containing special characters, such as the delimiter or
// quotechar, or which contain new-line characters. It defaults to '"'
//
// @param {Integer} skipInitialRows A integer number of rows to skip (default 0)
//
// Heavily based on uselesscode's JS CSV parser (MIT Licensed):
// http://www.uselesscode.org/javascript/csv/
my.parseCSV= function(s, options) {
@ -128,7 +140,7 @@ this.recline.Backend.CSV = this.recline.Backend.CSV || {};
// If we are at a EOF or EOR
if (inQuote === false && (cur === delimiter || cur === "\n")) {
field = processField(field);
field = processField(field);
// Add the current field to the current row
row.push(field);
// If this is EOR append row to output and flush row
@ -168,6 +180,9 @@ this.recline.Backend.CSV = this.recline.Backend.CSV || {};
row.push(field);
out.push(row);
// Expose the ability to discard initial rows
if (options.skipInitialRows) out = out.slice(options.skipInitialRows);
return out;
};

View File

@ -3,6 +3,7 @@ this.recline.Backend = this.recline.Backend || {};
this.recline.Backend.DataProxy = this.recline.Backend.DataProxy || {};
(function(my) {
"use strict";
my.__type__ = 'dataproxy';
// URL for the dataproxy
my.dataproxy_url = '//jsonpdataproxy.appspot.com';
@ -12,7 +13,7 @@ this.recline.Backend.DataProxy = this.recline.Backend.DataProxy || {};
// use either jQuery or Underscore Deferred depending on what is available
var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
var Deferred = (typeof jQuery !== "undefined" && jQuery.Deferred) || _.Deferred;
// ## load
//

View File

@ -1,285 +0,0 @@
this.recline = this.recline || {};
this.recline.Backend = this.recline.Backend || {};
this.recline.Backend.ElasticSearch = this.recline.Backend.ElasticSearch || {};
(function($, my) {
my.__type__ = 'elasticsearch';
// use either jQuery or Underscore Deferred depending on what is available
var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
// ## ElasticSearch Wrapper
//
// A simple JS wrapper around an [ElasticSearch](http://www.elasticsearch.org/) endpoints.
//
// @param {String} endpoint: url for ElasticSearch type/table, e.g. for ES running
// on http://localhost:9200 with index twitter and type tweet it would be:
//
// <pre>http://localhost:9200/twitter/tweet</pre>
//
// @param {Object} options: set of options such as:
//
// * headers - {dict of headers to add to each request}
// * dataType: dataType for AJAx requests e.g. set to jsonp to make jsonp requests (default is json requests)
my.Wrapper = function(endpoint, options) {
var self = this;
this.endpoint = endpoint;
this.options = _.extend({
dataType: 'json'
},
options);
// ### mapping
//
// Get ES mapping for this type/table
//
// @return promise compatible deferred object.
this.mapping = function() {
var schemaUrl = self.endpoint + '/_mapping';
var jqxhr = makeRequest({
url: schemaUrl,
dataType: this.options.dataType
});
return jqxhr;
};
// ### get
//
// Get record corresponding to specified id
//
// @return promise compatible deferred object.
this.get = function(id) {
var base = this.endpoint + '/' + id;
return makeRequest({
url: base,
dataType: 'json'
});
};
// ### upsert
//
// create / update a record to ElasticSearch backend
//
// @param {Object} doc an object to insert to the index.
// @return deferred supporting promise API
this.upsert = function(doc) {
var data = JSON.stringify(doc);
url = this.endpoint;
if (doc.id) {
url += '/' + doc.id;
}
return makeRequest({
url: url,
type: 'POST',
data: data,
dataType: 'json'
});
};
// ### delete
//
// Delete a record from the ElasticSearch backend.
//
// @param {Object} id id of object to delete
// @return deferred supporting promise API
this.remove = function(id) {
url = this.endpoint;
url += '/' + id;
return makeRequest({
url: url,
type: 'DELETE',
dataType: 'json'
});
};
this._normalizeQuery = function(queryObj) {
var self = this;
var queryInfo = (queryObj && queryObj.toJSON) ? queryObj.toJSON() : _.extend({}, queryObj);
var out = {
constant_score: {
query: {}
}
};
if (!queryInfo.q) {
out.constant_score.query = {
match_all: {}
};
} else {
out.constant_score.query = {
query_string: {
query: queryInfo.q
}
};
}
if (queryInfo.filters && queryInfo.filters.length) {
out.constant_score.filter = {
and: []
};
_.each(queryInfo.filters, function(filter) {
out.constant_score.filter.and.push(self._convertFilter(filter));
});
}
return out;
},
// convert from Recline sort structure to ES form
// http://www.elasticsearch.org/guide/reference/api/search/sort.html
this._normalizeSort = function(sort) {
var out = _.map(sort, function(sortObj) {
var _tmp = {};
var _tmp2 = _.clone(sortObj);
delete _tmp2['field'];
_tmp[sortObj.field] = _tmp2;
return _tmp;
});
return out;
},
this._convertFilter = function(filter) {
var out = {};
out[filter.type] = {}
if (filter.type === 'term') {
out.term[filter.field] = filter.term.toLowerCase();
} else if (filter.type === 'geo_distance') {
out.geo_distance[filter.field] = filter.point;
out.geo_distance.distance = filter.distance;
out.geo_distance.unit = filter.unit;
}
return out;
},
// ### query
//
// @return deferred supporting promise API
this.query = function(queryObj) {
var esQuery = (queryObj && queryObj.toJSON) ? queryObj.toJSON() : _.extend({}, queryObj);
esQuery.query = this._normalizeQuery(queryObj);
delete esQuery.q;
delete esQuery.filters;
if (esQuery.sort && esQuery.sort.length > 0) {
esQuery.sort = this._normalizeSort(esQuery.sort);
}
var data = {source: JSON.stringify(esQuery)};
var url = this.endpoint + '/_search';
var jqxhr = makeRequest({
url: url,
data: data,
dataType: this.options.dataType
});
return jqxhr;
}
};
// ## Recline Connectors
//
// Requires URL of ElasticSearch endpoint to be specified on the dataset
// via the url attribute.
// ES options which are passed through to `options` on Wrapper (see Wrapper for details)
my.esOptions = {};
// ### fetch
my.fetch = function(dataset) {
var es = new my.Wrapper(dataset.url, my.esOptions);
var dfd = new Deferred();
es.mapping().done(function(schema) {
if (!schema){
dfd.reject({'message':'Elastic Search did not return a mapping'});
return;
}
// only one top level key in ES = the type so we can ignore it
var key = _.keys(schema)[0];
var fieldData = _.map(schema[key].properties, function(dict, fieldName) {
dict.id = fieldName;
return dict;
});
dfd.resolve({
fields: fieldData
});
})
.fail(function(arguments) {
dfd.reject(arguments);
});
return dfd.promise();
};
// ### save
my.save = function(changes, dataset) {
var es = new my.Wrapper(dataset.url, my.esOptions);
if (changes.creates.length + changes.updates.length + changes.deletes.length > 1) {
var dfd = new Deferred();
msg = 'Saving more than one item at a time not yet supported';
alert(msg);
dfd.reject(msg);
return dfd.promise();
}
if (changes.creates.length > 0) {
return es.upsert(changes.creates[0]);
}
else if (changes.updates.length >0) {
return es.upsert(changes.updates[0]);
} else if (changes.deletes.length > 0) {
return es.remove(changes.deletes[0].id);
}
};
// ### query
my.query = function(queryObj, dataset) {
var dfd = new Deferred();
var es = new my.Wrapper(dataset.url, my.esOptions);
var jqxhr = es.query(queryObj);
jqxhr.done(function(results) {
var out = {
total: results.hits.total
};
out.hits = _.map(results.hits.hits, function(hit) {
if (!('id' in hit._source) && hit._id) {
hit._source.id = hit._id;
}
return hit._source;
});
if (results.facets) {
out.facets = results.facets;
}
dfd.resolve(out);
}).fail(function(errorObj) {
var out = {
title: 'Failed: ' + errorObj.status + ' code',
message: errorObj.responseText
};
dfd.reject(out);
});
return dfd.promise();
};
// ### makeRequest
//
// Just $.ajax but in any headers in the 'headers' attribute of this
// Backend instance. Example:
//
// <pre>
// var jqxhr = this._makeRequest({
// url: the-url
// });
// </pre>
var makeRequest = function(data, headers) {
var extras = {};
if (headers) {
extras = {
beforeSend: function(req) {
_.each(headers, function(value, key) {
req.setRequestHeader(key, value);
});
}
};
}
var data = _.extend(extras, data);
return $.ajax(data);
};
}(jQuery, this.recline.Backend.ElasticSearch));

View File

@ -1,167 +0,0 @@
this.recline = this.recline || {};
this.recline.Backend = this.recline.Backend || {};
this.recline.Backend.GDocs = this.recline.Backend.GDocs || {};
(function(my) {
my.__type__ = 'gdocs';
// use either jQuery or Underscore Deferred depending on what is available
var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
// ## Google spreadsheet backend
//
// Fetch data from a Google Docs spreadsheet.
//
// Dataset must have a url attribute pointing to the Gdocs or its JSON feed e.g.
// <pre>
// var dataset = new recline.Model.Dataset({
// url: 'https://docs.google.com/spreadsheet/ccc?key=0Aon3JiuouxLUdGlQVDJnbjZRSU1tUUJWOUZXRG53VkE#gid=0'
// },
// 'gdocs'
// );
//
// var dataset = new recline.Model.Dataset({
// url: 'https://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values?alt=json'
// },
// 'gdocs'
// );
// </pre>
//
// @return object with two attributes
//
// * fields: array of Field objects
// * records: array of objects for each row
my.fetch = function(dataset) {
var dfd = new Deferred();
var urls = my.getGDocsAPIUrls(dataset.url);
// TODO cover it with tests
// get the spreadsheet title
(function () {
var titleDfd = new Deferred();
jQuery.getJSON(urls.spreadsheet, function (d) {
titleDfd.resolve({
spreadsheetTitle: d.feed.title.$t
});
});
return titleDfd.promise();
}()).then(function (response) {
// get the actual worksheet data
jQuery.getJSON(urls.worksheet, function(d) {
var result = my.parseData(d);
var fields = _.map(result.fields, function(fieldId) {
return {id: fieldId};
});
dfd.resolve({
metadata: {
title: response.spreadsheetTitle +" :: "+ result.worksheetTitle,
spreadsheetTitle: response.spreadsheetTitle,
worksheetTitle : result.worksheetTitle
},
records : result.records,
fields : fields,
useMemoryStore: true
});
});
});
return dfd.promise();
};
// ## parseData
//
// Parse data from Google Docs API into a reasonable form
//
// :options: (optional) optional argument dictionary:
// columnsToUse: list of columns to use (specified by field names)
// colTypes: dictionary (with column names as keys) specifying types (e.g. range, percent for use in conversion).
// :return: tabular data object (hash with keys: field and data).
//
// Issues: seems google docs return columns in rows in random order and not even sure whether consistent across rows.
my.parseData = function(gdocsSpreadsheet, options) {
var options = options || {};
var colTypes = options.colTypes || {};
var results = {
fields : [],
records: []
};
var entries = gdocsSpreadsheet.feed.entry || [];
var key;
var colName;
// percentage values (e.g. 23.3%)
var rep = /^([\d\.\-]+)\%$/;
for(key in entries[0]) {
// it's barely possible it has inherited keys starting with 'gsx$'
if(/^gsx/.test(key)) {
colName = key.substr(4);
results.fields.push(colName);
}
}
// converts non numberical values that should be numerical (22.3%[string] -> 0.223[float])
results.records = _.map(entries, function(entry) {
var row = {};
_.each(results.fields, function(col) {
var _keyname = 'gsx$' + col;
var value = entry[_keyname].$t;
var num;
// TODO cover this part of code with test
// TODO use the regexp only once
// if labelled as % and value contains %, convert
if(colTypes[col] === 'percent' && rep.test(value)) {
num = rep.exec(value)[1];
value = parseFloat(num) / 100;
}
row[col] = value;
});
return row;
});
results.worksheetTitle = gdocsSpreadsheet.feed.title.$t;
return results;
};
// Convenience function to get GDocs JSON API Url from standard URL
my.getGDocsAPIUrls = function(url) {
// https://docs.google.com/spreadsheet/ccc?key=XXXX#gid=YYY
var regex = /.*spreadsheet\/ccc?.*key=([^#?&+]+)[^#]*(#gid=([\d]+).*)?/;
var matches = url.match(regex);
var key;
var worksheet;
var urls;
if(!!matches) {
key = matches[1];
// the gid in url is 0-based and feed url is 1-based
worksheet = parseInt(matches[3]) + 1;
if (isNaN(worksheet)) {
worksheet = 1;
}
urls = {
worksheet : 'https://spreadsheets.google.com/feeds/list/'+ key +'/'+ worksheet +'/public/values?alt=json',
spreadsheet: 'https://spreadsheets.google.com/feeds/worksheets/'+ key +'/public/basic?alt=json'
}
}
else {
// we assume that it's one of the feeds urls
key = url.split('/')[5];
// by default then, take first worksheet
worksheet = 1;
urls = {
worksheet : 'https://spreadsheets.google.com/feeds/list/'+ key +'/'+ worksheet +'/public/values?alt=json',
spreadsheet: 'https://spreadsheets.google.com/feeds/worksheets/'+ key +'/public/basic?alt=json'
}
}
return urls;
};
}(this.recline.Backend.GDocs));

View File

@ -3,10 +3,11 @@ this.recline.Backend = this.recline.Backend || {};
this.recline.Backend.Memory = this.recline.Backend.Memory || {};
(function(my) {
"use strict";
my.__type__ = 'memory';
// private data - use either jQuery or Underscore Deferred depending on what is available
var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
var Deferred = (typeof jQuery !== "undefined" && jQuery.Deferred) || _.Deferred;
// ## Data Wrapper
//
@ -107,9 +108,9 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
integer: function (e) { return parseFloat(e, 10); },
'float': function (e) { return parseFloat(e, 10); },
number: function (e) { return parseFloat(e, 10); },
string : function (e) { return e.toString() },
date : function (e) { return new Date(e).valueOf() },
datetime : function (e) { return new Date(e).valueOf() }
string : function (e) { return e.toString(); },
date : function (e) { return moment(e).valueOf(); },
datetime : function (e) { return new Date(e).valueOf(); }
};
var keyedFields = {};
_.each(self.fields, function(field) {
@ -140,19 +141,19 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
}
function range(record, filter) {
var startnull = (filter.start == null || filter.start === '');
var stopnull = (filter.stop == null || filter.stop === '');
var fromnull = (_.isUndefined(filter.from) || filter.from === null || filter.from === '');
var tonull = (_.isUndefined(filter.to) || filter.to === null || filter.to === '');
var parse = getDataParser(filter);
var value = parse(record[filter.field]);
var start = parse(filter.start);
var stop = parse(filter.stop);
var from = parse(fromnull ? '' : filter.from);
var to = parse(tonull ? '' : filter.to);
// if at least one end of range is set do not allow '' to get through
// note that for strings '' <= {any-character} e.g. '' <= 'a'
if ((!startnull || !stopnull) && value === '') {
if ((!fromnull || !tonull) && value === '') {
return false;
}
return ((startnull || value >= start) && (stopnull || value <= stop));
return ((fromnull || value >= from) && (tonull || value <= to));
}
function geo_distance() {
@ -165,8 +166,8 @@ this.recline.Backend.Memory = this.recline.Backend.Memory || {};
if (queryObj.q) {
var terms = queryObj.q.split(' ');
var patterns=_.map(terms, function(term) {
return new RegExp(term.toLowerCase());;
});
return new RegExp(term.toLowerCase());
});
results = _.filter(results, function(rawdoc) {
var matches = true;
_.each(patterns, function(pattern) {

View File

@ -3,9 +3,10 @@ this.recline = this.recline || {};
this.recline.Model = this.recline.Model || {};
(function(my) {
"use strict";
// use either jQuery or Underscore Deferred depending on what is available
var Deferred = _.isUndefined(this.jQuery) ? _.Deferred : jQuery.Deferred;
var Deferred = (typeof jQuery !== "undefined" && jQuery.Deferred) || _.Deferred;
// ## <a id="dataset">Dataset</a>
my.Dataset = Backbone.Model.extend({
@ -15,6 +16,7 @@ my.Dataset = Backbone.Model.extend({
// ### initialize
initialize: function() {
var self = this;
_.bindAll(this, 'query');
this.backend = null;
if (this.get('backend')) {
@ -34,17 +36,26 @@ my.Dataset = Backbone.Model.extend({
this.facets = new my.FacetList();
this.recordCount = null;
this.queryState = new my.Query();
this.queryState.bind('change', this.query);
this.queryState.bind('facet:add', this.query);
this.queryState.bind('change facet:add', function () {
self.query(); // We want to call query() without any arguments.
});
// store is what we query and save against
// store will either be the backend or be a memory store if Backend fetch
// tells us to use memory store
this._store = this.backend;
// if backend has a handleQueryResultFunction, use that
this._handleResult = (this.backend != null && _.has(this.backend, 'handleQueryResult')) ?
this.backend.handleQueryResult : this._handleQueryResult;
if (this.backend == recline.Backend.Memory) {
this.fetch();
}
},
sync: function(method, model, options) {
return this.backend.sync(method, model, options);
},
// ### fetch
//
// Retrieve dataset and (some) records from the backend.
@ -68,7 +79,13 @@ my.Dataset = Backbone.Model.extend({
}
function handleResults(results) {
var out = self._normalizeRecordsAndFields(results.records, results.fields);
// if explicitly given the fields
// (e.g. var dataset = new Dataset({fields: fields, ...})
// use that field info over anything we get back by parsing the data
// (results.fields)
var fields = self.get('fields') || results.fields;
var out = self._normalizeRecordsAndFields(results.records, fields);
if (results.useMemoryStore) {
self._store = new recline.Backend.Memory.Store(out.records, out.fields);
}
@ -180,7 +197,7 @@ my.Dataset = Backbone.Model.extend({
this._store.query(actualQuery, this.toJSON())
.done(function(queryResult) {
self._handleQueryResult(queryResult);
self._handleResult(queryResult);
self.trigger('query:done');
dfd.resolve(self.records);
})
@ -298,7 +315,7 @@ my.Record = Backbone.Model.extend({
//
// NB: if field is undefined a default '' value will be returned
getFieldValue: function(field) {
val = this.getFieldValueUnrendered(field);
var val = this.getFieldValueUnrendered(field);
if (field && !_.isUndefined(field.renderer)) {
val = field.renderer(val, field, this.toJSON());
}
@ -472,8 +489,8 @@ my.Query = Backbone.Model.extend({
},
range: {
type: 'range',
start: '',
stop: ''
from: '',
to: ''
},
geo_distance: {
type: 'geo_distance',
@ -501,6 +518,23 @@ my.Query = Backbone.Model.extend({
filters.push(ourfilter);
this.trigger('change:filters:new-blank');
},
replaceFilter: function(filter) {
// delete filter on the same field, then add
var filters = this.get('filters');
var idx = -1;
_.each(this.get('filters'), function(f, key, list) {
if (filter.field == f.field) {
idx = key;
}
});
// trigger just one event (change:filters:new-blank) instead of one for remove and
// one for add
if (idx >= 0) {
filters.splice(idx, 1);
this.set({filters: filters});
}
this.addFilter(filter);
},
updateFilter: function(index, value) {
},
// ### removeFilter
@ -517,7 +551,7 @@ my.Query = Backbone.Model.extend({
// Add a Facet to this query
//
// See <http://www.elasticsearch.org/guide/reference/api/search/facets/>
addFacet: function(fieldId) {
addFacet: function(fieldId, size, silent) {
var facets = this.get('facets');
// Assume id and fieldId should be the same (TODO: this need not be true if we want to add two different type of facets on same field)
if (_.contains(_.keys(facets), fieldId)) {
@ -526,8 +560,13 @@ my.Query = Backbone.Model.extend({
facets[fieldId] = {
terms: { field: fieldId }
};
if (!_.isUndefined(size)) {
facets[fieldId].terms.size = size;
}
this.set({facets: facets}, {silent: true});
this.trigger('facet:add', this);
if (!silent) {
this.trigger('facet:add', this);
}
},
addHistogramFacet: function(fieldId) {
var facets = this.get('facets');
@ -539,7 +578,30 @@ my.Query = Backbone.Model.extend({
};
this.set({facets: facets}, {silent: true});
this.trigger('facet:add', this);
},
removeFacet: function(fieldId) {
var facets = this.get('facets');
// Assume id and fieldId should be the same (TODO: this need not be true if we want to add two different type of facets on same field)
if (!_.contains(_.keys(facets), fieldId)) {
return;
}
delete facets[fieldId];
this.set({facets: facets}, {silent: true});
this.trigger('facet:remove', this);
},
clearFacets: function() {
var facets = this.get('facets');
_.each(_.keys(facets), function(fieldId) {
delete facets[fieldId];
});
this.trigger('facet:remove', this);
},
// trigger a facet add; use this to trigger a single event after adding
// multiple facets
refreshFacets: function() {
this.trigger('facet:add', this);
}
});
@ -577,9 +639,9 @@ my.ObjectState = Backbone.Model.extend({
// ## Backbone.sync
//
// Override Backbone.sync to hand off to sync function in relevant backend
Backbone.sync = function(method, model, options) {
return model.backend.sync(method, model, options);
};
// Backbone.sync = function(method, model, options) {
// return model.backend.sync(method, model, options);
// };
}(this.recline.Model));

View File

@ -4,7 +4,7 @@ this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
// ## Graph view for a Dataset using Flot graphing library.
//
// Initialization arguments (in a hash in first parameter):
@ -15,7 +15,8 @@ this.recline.View = this.recline.View || {};
// {
// group: {column name for x-axis},
// series: [{column name for series A}, {column name series B}, ... ],
// graphType: 'line',
// // options are: lines, points, lines-and-points, bars, columns
// graphType: 'lines',
// graphOptions: {custom [flot options]}
// }
//
@ -38,14 +39,11 @@ my.Flot = Backbone.View.extend({
var self = this;
this.graphColors = ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"];
this.el = $(this.el);
_.bindAll(this, 'render', 'redraw', '_toolTip', '_xaxisLabel');
this.needToRedraw = false;
this.model.bind('change', this.render);
this.model.fields.bind('reset', this.render);
this.model.fields.bind('add', this.render);
this.model.records.bind('add', this.redraw);
this.model.records.bind('reset', this.redraw);
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model.fields, 'reset add', this.render);
this.listenTo(this.model.records, 'reset add', this.redraw);
var stateData = _.extend({
group: null,
// so that at least one series chooser box shows up
@ -60,29 +58,34 @@ my.Flot = Backbone.View.extend({
model: this.model,
state: this.state.toJSON()
});
this.editor.state.bind('change', function() {
this.listenTo(this.editor.state, 'change', function() {
self.state.set(self.editor.state.toJSON());
self.redraw();
});
this.elSidebar = this.editor.el;
this.elSidebar = this.editor.$el;
},
render: function() {
var self = this;
var tmplData = this.model.toTemplateJSON();
var htmls = Mustache.render(this.template, tmplData);
$(this.el).html(htmls);
this.$graph = this.el.find('.panel.graph');
this.$el.html(htmls);
this.$graph = this.$el.find('.panel.graph');
this.$graph.on("plothover", this._toolTip);
return this;
},
remove: function () {
this.editor.remove();
Backbone.View.prototype.remove.apply(this, arguments);
},
redraw: function() {
// There are issues generating a Flot graph if either:
// * The relevant div that graph attaches to his hidden at the moment of creating the plot -- Flot will complain with
// Uncaught Invalid dimensions for plot, width = 0, height = 0
// * There is no data for the plot -- either same error or may have issues later with errors like 'non-existent node-value'
var areWeVisible = !jQuery.expr.filters.hidden(this.el[0]);
var areWeVisible = !jQuery.expr.filters.hidden(this.el);
if ((!areWeVisible || this.model.records.length === 0)) {
this.needToRedraw = true;
return;
@ -153,25 +156,17 @@ my.Flot = Backbone.View.extend({
},
_xaxisLabel: function (x) {
var xfield = this.model.fields.get(this.state.attributes.group);
// time series
var xtype = xfield.get('type');
var isDateTime = (xtype === 'date' || xtype === 'date-time' || xtype === 'time');
if (this.xvaluesAreIndex) {
if (this._groupFieldIsDateTime()) {
// oddly x comes through as milliseconds *string* (rather than int
// or float) so we have to reparse
x = new Date(parseFloat(x)).toLocaleDateString();
} else if (this.xvaluesAreIndex) {
x = parseInt(x, 10);
// HACK: deal with bar graph style cases where x-axis items were strings
// In this case x at this point is the index of the item in the list of
// records not its actual x-axis value
x = this.model.records.models[x].get(this.state.attributes.group);
}
if (isDateTime) {
x = new Date(x).toLocaleDateString();
}
// } else if (isDateTime) {
// x = new Date(parseInt(x, 10)).toLocaleDateString();
// }
return x;
},
@ -186,25 +181,26 @@ my.Flot = Backbone.View.extend({
// @param numPoints the number of points that will be plotted
getGraphOptions: function(typeId, numPoints) {
var self = this;
var tickFormatter = function (x) {
// convert x to a string and make sure that it is not too long or the
// tick labels will overlap
// TODO: find a more accurate way of calculating the size of tick labels
var label = self._xaxisLabel(x) || "";
if (typeof label !== 'string') {
label = label.toString();
}
if (self.state.attributes.graphType !== 'bars' && label.length > 10) {
label = label.slice(0, 10) + "...";
}
return label;
};
var groupFieldIsDateTime = self._groupFieldIsDateTime();
var xaxis = {};
xaxis.tickFormatter = tickFormatter;
if (!groupFieldIsDateTime) {
xaxis.tickFormatter = function (x) {
// convert x to a string and make sure that it is not too long or the
// tick labels will overlap
// TODO: find a more accurate way of calculating the size of tick labels
var label = self._xaxisLabel(x) || "";
if (typeof label !== 'string') {
label = label.toString();
}
if (self.state.attributes.graphType !== 'bars' && label.length > 10) {
label = label.slice(0, 10) + "...";
}
return label;
};
}
// for labels case we only want ticks at the label intervals
// HACK: however we also get this case with Date fields. In that case we
@ -213,10 +209,12 @@ my.Flot = Backbone.View.extend({
var numTicks = Math.min(this.model.records.length, 15);
var increment = this.model.records.length / numTicks;
var ticks = [];
for (i=0; i<numTicks; i++) {
for (var i=0; i<numTicks; i++) {
ticks.push(parseInt(i*increment, 10));
}
xaxis.ticks = ticks;
} else if (groupFieldIsDateTime) {
xaxis.mode = 'time';
}
var yaxis = {};
@ -298,34 +296,54 @@ my.Flot = Backbone.View.extend({
}
},
_groupFieldIsDateTime: function() {
var xfield = this.model.fields.get(this.state.attributes.group);
var xtype = xfield.get('type');
var isDateTime = (xtype === 'date' || xtype === 'date-time' || xtype === 'time');
return isDateTime;
},
createSeries: function() {
var self = this;
self.xvaluesAreIndex = false;
var series = [];
var xfield = self.model.fields.get(self.state.attributes.group);
var isDateTime = self._groupFieldIsDateTime();
_.each(this.state.attributes.series, function(field) {
var points = [];
var fieldLabel = self.model.fields.get(field).get('label');
if (isDateTime){
var cast = function(x){
var _date = moment(String(x));
if (_date.isValid()) {
x = _date.toDate().getTime();
}
return x
}
} else {
var raw = _.map(self.model.records.models,
function(doc, index){
return doc.getFieldValueUnrendered(xfield)
});
if (_.all(raw, function(x){ return !isNaN(parseFloat(x)) })){
var cast = function(x){ return parseFloat(x) }
} else {
self.xvaluesAreIndex = true
}
}
_.each(self.model.records.models, function(doc, index) {
var xfield = self.model.fields.get(self.state.attributes.group);
var x = doc.getFieldValue(xfield);
// time series
var xtype = xfield.get('type');
var isDateTime = (xtype === 'date' || xtype === 'date-time' || xtype === 'time');
if (isDateTime) {
self.xvaluesAreIndex = true;
x = index;
} else if (typeof x === 'string') {
x = parseFloat(x);
if (isNaN(x)) { // assume this is a string label
x = index;
self.xvaluesAreIndex = true;
}
if(self.xvaluesAreIndex){
var x = index;
}else{
var x = cast(doc.getFieldValueUnrendered(xfield));
}
var yfield = self.model.fields.get(field);
var y = doc.getFieldValue(yfield);
var y = doc.getFieldValueUnrendered(yfield);
if (self.state.attributes.graphType == 'bars') {
points.push([y, x]);
@ -403,10 +421,8 @@ my.FlotControls = Backbone.View.extend({
initialize: function(options) {
var self = this;
this.el = $(this.el);
_.bindAll(this, 'render');
this.model.fields.bind('reset', this.render);
this.model.fields.bind('add', this.render);
this.listenTo(this.model.fields, 'reset add', this.render);
this.state = new recline.Model.ObjectState(options.state);
this.render();
},
@ -415,7 +431,7 @@ my.FlotControls = Backbone.View.extend({
var self = this;
var tmplData = this.model.toTemplateJSON();
var htmls = Mustache.render(this.template, tmplData);
this.el.html(htmls);
this.$el.html(htmls);
// set up editor from state
if (this.state.get('graphType')) {
@ -439,7 +455,7 @@ my.FlotControls = Backbone.View.extend({
// Private: Helper function to select an option from a select list
//
_selectOption: function(id,value){
var options = this.el.find(id + ' select > option');
var options = this.$el.find(id + ' select > option');
if (options) {
options.each(function(opt){
if (this.value == value) {
@ -451,16 +467,16 @@ my.FlotControls = Backbone.View.extend({
},
onEditorSubmit: function(e) {
var select = this.el.find('.editor-group select');
var select = this.$el.find('.editor-group select');
var $editor = this;
var $series = this.el.find('.editor-series select');
var $series = this.$el.find('.editor-series select');
var series = $series.map(function () {
return $(this).val();
});
var updatedState = {
series: $.makeArray(series),
group: this.el.find('.editor-group select').val(),
graphType: this.el.find('.editor-type select').val()
group: this.$el.find('.editor-group select').val(),
graphType: this.$el.find('.editor-type select').val()
};
this.state.set(updatedState);
},
@ -477,7 +493,7 @@ my.FlotControls = Backbone.View.extend({
}, this.model.toTemplateJSON());
var htmls = Mustache.render(this.templateSeriesEditor, data);
this.el.find('.editor-series-group').append(htmls);
this.$el.find('.editor-series-group').append(htmls);
return this;
},

View File

@ -4,6 +4,7 @@ this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
// ## (Data) Grid Dataset View
//
// Provides a tabular view on a Dataset.
@ -15,11 +16,8 @@ my.Grid = Backbone.View.extend({
initialize: function(modelEtc) {
var self = this;
this.el = $(this.el);
_.bindAll(this, 'render', 'onHorizontalScroll');
this.model.records.bind('add', this.render);
this.model.records.bind('reset', this.render);
this.model.records.bind('remove', this.render);
this.listenTo(this.model.records, 'add reset remove', this.render);
this.tempState = {};
var state = _.extend({
hiddenFields: []
@ -59,7 +57,7 @@ my.Grid = Backbone.View.extend({
onHorizontalScroll: function(e) {
var currentScroll = $(e.target).scrollLeft();
this.el.find('.recline-grid thead tr').scrollLeft(currentScroll);
this.$el.find('.recline-grid thead tr').scrollLeft(currentScroll);
},
// ======================================================
@ -87,7 +85,7 @@ my.Grid = Backbone.View.extend({
var modelData = this.model.toJSON();
modelData.notEmpty = ( this.fields.length > 0 );
// TODO: move this sort of thing into a toTemplateJSON method on Dataset?
modelData.fields = _.map(this.fields, function(field) {
modelData.fields = this.fields.map(function(field) {
return field.toJSON();
});
// last header width = scroll bar - border (2px) */
@ -96,17 +94,18 @@ my.Grid = Backbone.View.extend({
},
render: function() {
var self = this;
this.fields = this.model.fields.filter(function(field) {
this.fields = new recline.Model.FieldList(this.model.fields.filter(function(field) {
return _.indexOf(self.state.get('hiddenFields'), field.id) == -1;
});
}));
this.scrollbarDimensions = this.scrollbarDimensions || this._scrollbarSize(); // skip measurement if already have dimensions
var numFields = this.fields.length;
// compute field widths (-20 for first menu col + 10px for padding on each col and finally 16px for the scrollbar)
var fullWidth = self.el.width() - 20 - 10 * numFields - this.scrollbarDimensions.width;
var fullWidth = self.$el.width() - 20 - 10 * numFields - this.scrollbarDimensions.width;
var width = parseInt(Math.max(50, fullWidth / numFields), 10);
// if columns extend outside viewport then remainder is 0
var remainder = Math.max(fullWidth - numFields * width,0);
_.each(this.fields, function(field, idx) {
this.fields.each(function(field, idx) {
// add the remainder to the first field width so we make up full col
if (idx === 0) {
field.set({width: width+remainder});
@ -115,10 +114,10 @@ my.Grid = Backbone.View.extend({
}
});
var htmls = Mustache.render(this.template, this.toTemplateJSON());
this.el.html(htmls);
this.$el.html(htmls);
this.model.records.forEach(function(doc) {
var tr = $('<tr />');
self.el.find('tbody').append(tr);
self.$el.find('tbody').append(tr);
var newView = new my.GridRow({
model: doc,
el: tr,
@ -127,12 +126,12 @@ my.Grid = Backbone.View.extend({
newView.render();
});
// hide extra header col if no scrollbar to avoid unsightly overhang
var $tbody = this.el.find('tbody')[0];
var $tbody = this.$el.find('tbody')[0];
if ($tbody.scrollHeight <= $tbody.offsetHeight) {
this.el.find('th.last-header').hide();
this.$el.find('th.last-header').hide();
}
this.el.find('.recline-grid').toggleClass('no-hidden', (self.state.get('hiddenFields').length === 0));
this.el.find('.recline-grid tbody').scroll(this.onHorizontalScroll);
this.$el.find('.recline-grid').toggleClass('no-hidden', (self.state.get('hiddenFields').length === 0));
this.$el.find('.recline-grid tbody').scroll(this.onHorizontalScroll);
return this;
},
@ -168,8 +167,7 @@ my.GridRow = Backbone.View.extend({
initialize: function(initData) {
_.bindAll(this, 'render');
this._fields = initData.fields;
this.el = $(this.el);
this.model.bind('change', this.render);
this.listenTo(this.model, 'change', this.render);
},
template: ' \
@ -202,9 +200,9 @@ my.GridRow = Backbone.View.extend({
},
render: function() {
this.el.attr('data-id', this.model.id);
this.$el.attr('data-id', this.model.id);
var html = Mustache.render(this.template, this.toTemplateJSON());
$(this.el).html(html);
this.$el.html(html);
return this;
},
@ -224,7 +222,7 @@ my.GridRow = Backbone.View.extend({
',
onEditClick: function(e) {
var editing = this.el.find('.data-table-cell-editor-editor');
var editing = this.$el.find('.data-table-cell-editor-editor');
if (editing.length > 0) {
editing.parents('.data-table-cell-value').html(editing.text()).siblings('.data-table-cell-edit').removeClass("hidden");
}

View File

@ -4,13 +4,13 @@ this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
// ## Map view for a Dataset using Leaflet mapping library.
//
// This view allows to plot gereferenced records on a map. The location
// information can be provided in 2 ways:
//
// 1. Via a single field. This field must be either a geo_point or
// 1. Via a single field. This field must be either a geo_point or
// [GeoJSON](http://geojson.org) object
// 2. Via two fields with latitude and longitude coordinates.
//
@ -28,6 +28,8 @@ this.recline.View = this.recline.View || {};
// latField: {id of field containing latitude in the dataset}
// autoZoom: true,
// // use cluster support
// // cluster: true = always on
// // cluster: false = always off
// cluster: false
// }
// </pre>
@ -51,7 +53,6 @@ my.Map = Backbone.View.extend({
initialize: function(options) {
var self = this;
this.el = $(this.el);
this.visible = true;
this.mapReady = false;
// this will be the Leaflet L.Map object (setup below)
@ -78,32 +79,32 @@ my.Map = Backbone.View.extend({
};
// Listen to changes in the fields
this.model.fields.bind('change', function() {
this.listenTo(this.model.fields, 'change', function() {
self._setupGeometryField();
self.render();
});
// Listen to changes in the records
this.model.records.bind('add', function(doc){self.redraw('add',doc);});
this.model.records.bind('change', function(doc){
this.listenTo(this.model.records, 'add', function(doc){self.redraw('add',doc);});
this.listenTo(this.model.records, 'change', function(doc){
self.redraw('remove',doc);
self.redraw('add',doc);
});
this.model.records.bind('remove', function(doc){self.redraw('remove',doc);});
this.model.records.bind('reset', function(){self.redraw('reset');});
this.listenTo(this.model.records, 'remove', function(doc){self.redraw('remove',doc);});
this.listenTo(this.model.records, 'reset', function(){self.redraw('reset');});
this.menu = new my.MapMenu({
model: this.model,
state: this.state.toJSON()
});
this.menu.state.bind('change', function() {
this.listenTo(this.menu.state, 'change', function() {
self.state.set(self.menu.state.toJSON());
self.redraw();
});
this.state.bind('change', function() {
this.listenTo(this.state, 'change', function() {
self.redraw();
});
this.elSidebar = this.menu.el;
this.elSidebar = this.menu.$el;
},
// ## Customization Functions
@ -123,7 +124,7 @@ my.Map = Backbone.View.extend({
// }
infobox: function(record) {
var html = '';
for (key in record.attributes){
for (var key in record.attributes){
if (!(this.state.get('geomField') && key == this.state.get('geomField'))){
html += '<div><strong>' + key + '</strong>: '+ record.attributes[key] + '</div>';
}
@ -172,10 +173,9 @@ my.Map = Backbone.View.extend({
// Also sets up the editor fields and the map if necessary.
render: function() {
var self = this;
htmls = Mustache.render(this.template, this.model.toTemplateJSON());
$(this.el).html(htmls);
this.$map = this.el.find('.panel.map');
var htmls = Mustache.render(this.template, this.model.toTemplateJSON());
this.$el.html(htmls);
this.$map = this.$el.find('.panel.map');
this.redraw();
return this;
},
@ -219,15 +219,6 @@ my.Map = Backbone.View.extend({
this._remove(doc);
}
// enable clustering if there is a large number of markers
var countAfter = 0;
this.features.eachLayer(function(){countAfter++;});
var sizeIncreased = countAfter - countBefore > 0;
if (!this.state.get('cluster') && countAfter > 64 && sizeIncreased) {
this.state.set({cluster: true});
return;
}
// this must come before zooming!
// if not: errors when using e.g. circle markers like
// "Cannot call method 'project' of undefined"
@ -326,7 +317,7 @@ my.Map = Backbone.View.extend({
if (!(docs instanceof Array)) docs = [docs];
_.each(docs,function(doc){
for (key in self.features._layers){
for (var key in self.features._layers){
if (self.features._layers[key].feature.properties.cid == doc.cid){
self.features.removeLayer(self.features._layers[key]);
}
@ -335,6 +326,31 @@ my.Map = Backbone.View.extend({
},
// Private: convert DMS coordinates to decimal
//
// north and east are positive, south and west are negative
//
_parseCoordinateString: function(coord){
if (typeof(coord) != 'string') {
return(parseFloat(coord));
}
var dms = coord.split(/[^\.\d\w]+/);
var deg = 0; var m = 0;
var toDeg = [1, 60, 3600]; // conversion factors for Deg, min, sec
var i;
for (i = 0; i < dms.length; ++i) {
if (isNaN(parseFloat(dms[i]))) {
continue;
}
deg += parseFloat(dms[i]) / toDeg[m];
m += 1;
}
if (coord.match(/[SW]/)) {
deg = -1*deg;
}
return(deg);
},
// Private: Return a GeoJSON geomtry extracted from the record fields
//
_getGeometryFromRecord: function(doc){
@ -346,12 +362,12 @@ my.Map = Backbone.View.extend({
value = $.parseJSON(value);
} catch(e) {}
}
if (typeof(value) === 'string') {
value = value.replace('(', '').replace(')', '');
var parts = value.split(',');
var lat = parseFloat(parts[0]);
var lon = parseFloat(parts[1]);
var lat = this._parseCoordinateString(parts[0]);
var lon = this._parseCoordinateString(parts[1]);
if (!isNaN(lon) && !isNaN(parseFloat(lat))) {
return {
"type": "Point",
@ -379,6 +395,9 @@ my.Map = Backbone.View.extend({
// We'll create a GeoJSON like point object from the two lat/lon fields
var lon = doc.get(this.state.get('lonField'));
var lat = doc.get(this.state.get('latField'));
lon = this._parseCoordinateString(lon);
lat = this._parseCoordinateString(lat);
if (!isNaN(parseFloat(lon)) && !isNaN(parseFloat(lat))) {
return {
type: 'Point',
@ -442,8 +461,8 @@ my.Map = Backbone.View.extend({
var self = this;
this.map = new L.Map(this.$map.get(0));
var mapUrl = "http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png";
var osmAttribution = 'Map data &copy; 2011 OpenStreetMap contributors, Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="http://developer.mapquest.com/content/osm/mq_logo.png">';
var mapUrl = "//otile{s}-s.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png";
var osmAttribution = 'Map data &copy; 2011 OpenStreetMap contributors, Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a> <img src="//developer.mapquest.com/content/osm/mq_logo.png">';
var bg = new L.TileLayer(mapUrl, {maxZoom: 18, attribution: osmAttribution ,subdomains: '1234'});
this.map.addLayer(bg);
@ -533,7 +552,6 @@ my.MapMenu = Backbone.View.extend({
Cluster markers</label> \
</div> \
<input type="hidden" class="editor-id" value="map-1" /> \
</div> \
</form> \
',
@ -547,11 +565,10 @@ my.MapMenu = Backbone.View.extend({
initialize: function(options) {
var self = this;
this.el = $(this.el);
_.bindAll(this, 'render');
this.model.fields.bind('change', this.render);
this.listenTo(this.model.fields, 'change', this.render);
this.state = new recline.Model.ObjectState(options.state);
this.state.bind('change', this.render);
this.listenTo(this.state, 'change', this.render);
this.render();
},
@ -560,28 +577,28 @@ my.MapMenu = Backbone.View.extend({
// Also sets up the editor fields and the map if necessary.
render: function() {
var self = this;
htmls = Mustache.render(this.template, this.model.toTemplateJSON());
$(this.el).html(htmls);
var htmls = Mustache.render(this.template, this.model.toTemplateJSON());
this.$el.html(htmls);
if (this._geomReady() && this.model.fields.length){
if (this.state.get('geomField')){
this._selectOption('editor-geom-field',this.state.get('geomField'));
this.el.find('#editor-field-type-geom').attr('checked','checked').change();
this.$el.find('#editor-field-type-geom').attr('checked','checked').change();
} else{
this._selectOption('editor-lon-field',this.state.get('lonField'));
this._selectOption('editor-lat-field',this.state.get('latField'));
this.el.find('#editor-field-type-latlon').attr('checked','checked').change();
this.$el.find('#editor-field-type-latlon').attr('checked','checked').change();
}
}
if (this.state.get('autoZoom')) {
this.el.find('#editor-auto-zoom').attr('checked', 'checked');
this.$el.find('#editor-auto-zoom').attr('checked', 'checked');
} else {
this.el.find('#editor-auto-zoom').removeAttr('checked');
this.$el.find('#editor-auto-zoom').removeAttr('checked');
}
if (this.state.get('cluster')) {
this.el.find('#editor-cluster').attr('checked', 'checked');
this.$el.find('#editor-cluster').attr('checked', 'checked');
} else {
this.el.find('#editor-cluster').removeAttr('checked');
this.$el.find('#editor-cluster').removeAttr('checked');
}
return this;
},
@ -600,17 +617,17 @@ my.MapMenu = Backbone.View.extend({
//
onEditorSubmit: function(e){
e.preventDefault();
if (this.el.find('#editor-field-type-geom').attr('checked')){
if (this.$el.find('#editor-field-type-geom').attr('checked')){
this.state.set({
geomField: this.el.find('.editor-geom-field > select > option:selected').val(),
geomField: this.$el.find('.editor-geom-field > select > option:selected').val(),
lonField: null,
latField: null
});
} else {
this.state.set({
geomField: null,
lonField: this.el.find('.editor-lon-field > select > option:selected').val(),
latField: this.el.find('.editor-lat-field > select > option:selected').val()
lonField: this.$el.find('.editor-lon-field > select > option:selected').val(),
latField: this.$el.find('.editor-lat-field > select > option:selected').val()
});
}
return false;
@ -621,11 +638,11 @@ my.MapMenu = Backbone.View.extend({
//
onFieldTypeChange: function(e){
if (e.target.value == 'geom'){
this.el.find('.editor-field-type-geom').show();
this.el.find('.editor-field-type-latlon').hide();
this.$el.find('.editor-field-type-geom').show();
this.$el.find('.editor-field-type-latlon').hide();
} else {
this.el.find('.editor-field-type-geom').hide();
this.el.find('.editor-field-type-latlon').show();
this.$el.find('.editor-field-type-geom').hide();
this.$el.find('.editor-field-type-latlon').show();
}
},
@ -640,7 +657,7 @@ my.MapMenu = Backbone.View.extend({
// Private: Helper function to select an option from a select list
//
_selectOption: function(id,value){
var options = this.el.find('.' + id + ' > select > option');
var options = this.$el.find('.' + id + ' > select > option');
if (options){
options.each(function(opt){
if (this.value == value) {

View File

@ -5,6 +5,7 @@ this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
// ## MultiView
//
// Manage multiple views together along with query editor etc. Usage:
@ -129,7 +130,6 @@ my.MultiView = Backbone.View.extend({
initialize: function(options) {
var self = this;
this.el = $(this.el);
this._setupState(options.state);
// Hash of 'page' views (i.e. those for whole page) keyed by page name
@ -199,43 +199,39 @@ my.MultiView = Backbone.View.extend({
}
this._showHideSidebar();
this.model.bind('query:start', function() {
self.notify({loader: true, persist: true});
});
this.model.bind('query:done', function() {
self.clearNotifications();
self.el.find('.doc-count').text(self.model.recordCount || 'Unknown');
});
this.model.bind('query:fail', function(error) {
self.clearNotifications();
var msg = '';
if (typeof(error) == 'string') {
msg = error;
} else if (typeof(error) == 'object') {
if (error.title) {
msg = error.title + ': ';
}
if (error.message) {
msg += error.message;
}
} else {
msg = 'There was an error querying the backend';
this.listenTo(this.model, 'query:start', function() {
self.notify({loader: true, persist: true});
});
this.listenTo(this.model, 'query:done', function() {
self.clearNotifications();
self.$el.find('.doc-count').text(self.model.recordCount || 'Unknown');
});
this.listenTo(this.model, 'query:fail', function(error) {
self.clearNotifications();
var msg = '';
if (typeof(error) == 'string') {
msg = error;
} else if (typeof(error) == 'object') {
if (error.title) {
msg = error.title + ': ';
}
self.notify({message: msg, category: 'error', persist: true});
});
if (error.message) {
msg += error.message;
}
} else {
msg = 'There was an error querying the backend';
}
self.notify({message: msg, category: 'error', persist: true});
});
// retrieve basic data like fields etc
// note this.model and dataset returned are the same
// TODO: set query state ...?
this.model.queryState.set(self.state.get('query'), {silent: true});
this.model.fetch()
.fail(function(error) {
self.notify({message: error.message, category: 'error', persist: true});
});
},
setReadOnly: function() {
this.el.addClass('recline-read-only');
this.$el.addClass('recline-read-only');
},
render: function() {
@ -243,11 +239,11 @@ my.MultiView = Backbone.View.extend({
tmplData.views = this.pageViews;
tmplData.sidebarViews = this.sidebarViews;
var template = Mustache.render(this.template, tmplData);
$(this.el).html(template);
this.$el.html(template);
// now create and append other views
var $dataViewContainer = this.el.find('.data-view-container');
var $dataSidebar = this.el.find('.data-view-sidebar');
var $dataViewContainer = this.$el.find('.data-view-container');
var $dataSidebar = this.$el.find('.data-view-sidebar');
// the main views
_.each(this.pageViews, function(view, pageName) {
@ -259,25 +255,37 @@ my.MultiView = Backbone.View.extend({
});
_.each(this.sidebarViews, function(view) {
this['$'+view.id] = view.view.el;
this['$'+view.id] = view.view.$el;
$dataSidebar.append(view.view.el);
}, this);
var pager = new recline.View.Pager({
this.pager = new recline.View.Pager({
model: this.model.queryState
});
this.el.find('.recline-results-info').after(pager.el);
this.$el.find('.recline-results-info').after(this.pager.el);
var queryEditor = new recline.View.QueryEditor({
this.queryEditor = new recline.View.QueryEditor({
model: this.model.queryState
});
this.el.find('.query-editor-here').append(queryEditor.el);
this.$el.find('.query-editor-here').append(this.queryEditor.el);
},
remove: function () {
_.each(this.pageViews, function (view) {
view.view.remove();
});
_.each(this.sidebarViews, function (view) {
view.view.remove();
});
this.pager.remove();
this.queryEditor.remove();
Backbone.View.prototype.remove.apply(this, arguments);
},
// hide the sidebar if empty
_showHideSidebar: function() {
var $dataSidebar = this.el.find('.data-view-sidebar');
var $dataSidebar = this.$el.find('.data-view-sidebar');
var visibleChildren = $dataSidebar.children().filter(function() {
return $(this).css("display") != "none";
}).length;
@ -290,19 +298,19 @@ my.MultiView = Backbone.View.extend({
},
updateNav: function(pageName) {
this.el.find('.navigation a').removeClass('active');
var $el = this.el.find('.navigation a[data-view="' + pageName + '"]');
this.$el.find('.navigation a').removeClass('active');
var $el = this.$el.find('.navigation a[data-view="' + pageName + '"]');
$el.addClass('active');
// add/remove sidebars and hide inactive views
_.each(this.pageViews, function(view, idx) {
if (view.id === pageName) {
view.view.el.show();
view.view.$el.show();
if (view.view.elSidebar) {
view.view.elSidebar.show();
}
} else {
view.view.el.hide();
view.view.$el.hide();
if (view.view.elSidebar) {
view.view.elSidebar.hide();
}
@ -371,7 +379,7 @@ my.MultiView = Backbone.View.extend({
_bindStateChanges: function() {
var self = this;
// finally ensure we update our state object when state of sub-object changes so that state is always up to date
this.model.queryState.bind('change', function() {
this.listenTo(this.model.queryState, 'change', function() {
self.state.set({query: self.model.queryState.toJSON()});
});
_.each(this.pageViews, function(pageView) {
@ -379,7 +387,7 @@ my.MultiView = Backbone.View.extend({
var update = {};
update['view-' + pageView.id] = pageView.view.state.toJSON();
self.state.set(update);
pageView.view.state.bind('change', function() {
self.listenTo(pageView.view.state, 'change', function() {
var update = {};
update['view-' + pageView.id] = pageView.view.state.toJSON();
// had problems where change not being triggered for e.g. grid view so let's do it explicitly
@ -393,7 +401,7 @@ my.MultiView = Backbone.View.extend({
_bindFlashNotifications: function() {
var self = this;
_.each(this.pageViews, function(pageView) {
pageView.view.bind('recline:flash', function(flash) {
self.listenTo(pageView.view, 'recline:flash', function(flash) {
self.notify(flash);
});
});
@ -519,7 +527,7 @@ my.parseQueryString = function(q) {
// Parse the query string out of the URL hash
my.parseHashQueryString = function() {
q = my.parseHashUrl(window.location.hash).query;
var q = my.parseHashUrl(window.location.hash).query;
return my.parseQueryString(q);
};

View File

@ -4,6 +4,7 @@ this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
// ## SlickGrid Dataset View
//
// Provides a tabular view on a Dataset, based on SlickGrid.
@ -22,7 +23,11 @@ this.recline.View = this.recline.View || {};
// model: dataset,
// el: $el,
// state: {
// gridOptions: {editable: true},
// gridOptions: {
// editable: true,
// enableAddRows: true
// ...
// },
// columnsEditor: [
// {column: 'date', editor: Slick.Editors.Date },
// {column: 'title', editor: Slick.Editors.Text}
@ -33,13 +38,10 @@ this.recline.View = this.recline.View || {};
my.SlickGrid = Backbone.View.extend({
initialize: function(modelEtc) {
var self = this;
this.el = $(this.el);
this.el.addClass('recline-slickgrid');
_.bindAll(this, 'render');
this.model.records.bind('add', this.render);
this.model.records.bind('reset', this.render);
this.model.records.bind('remove', this.render);
this.model.records.bind('change', this.onRecordChanged, this);
this.$el.addClass('recline-slickgrid');
_.bindAll(this, 'render', 'onRecordChanged');
this.listenTo(this.model.records, 'add remove reset', this.render);
this.listenTo(this.model.records, 'change', this.onRecordChanged);
var state = _.extend({
hiddenColumns: [],
@ -53,6 +55,8 @@ my.SlickGrid = Backbone.View.extend({
);
this.state = new recline.Model.ObjectState(state);
this._slickHandler = new Slick.EventHandler();
},
events: {
@ -113,12 +117,30 @@ my.SlickGrid = Backbone.View.extend({
var editInfo = _.find(self.state.get('columnsEditor'),function(c){return c.column === field.id;});
if (editInfo){
column.editor = editInfo.editor;
} else {
// guess editor type
var typeToEditorMap = {
'string': Slick.Editors.LongText,
'integer': Slick.Editors.IntegerEditor,
'number': Slick.Editors.Text,
// TODO: need a way to ensure we format date in the right way
// Plus what if dates are in distant past or future ... (?)
// 'date': Slick.Editors.DateEditor,
'date': Slick.Editors.Text,
'boolean': Slick.Editors.YesNoSelectEditor
// TODO: (?) percent ...
};
if (field.type in typeToEditorMap) {
column.editor = typeToEditorMap[field.type]
} else {
column.editor = Slick.Editors.LongText;
}
}
columns.push(column);
});
// Restrict the visible columns
var visibleColumns = columns.filter(function(column) {
var visibleColumns = _.filter(columns, function(column) {
return _.indexOf(self.state.get('hiddenColumns'), column.id) === -1;
});
@ -164,7 +186,7 @@ my.SlickGrid = Backbone.View.extend({
this.getItem = function(index) {return rows[index];};
this.getItemMetadata = function(index) {return {};};
this.getModel = function(index) {return models[index];};
this.getModelRow = function(m) {return models.indexOf(m);};
this.getModelRow = function(m) {return _.indexOf(models, m);};
this.updateItem = function(m,i) {
rows[i] = toRow(m);
models[i] = m;
@ -187,7 +209,7 @@ my.SlickGrid = Backbone.View.extend({
this.grid.setSortColumn(column, sortAsc);
}
this.grid.onSort.subscribe(function(e, args){
this._slickHandler.subscribe(this.grid.onSort, function(e, args){
var order = (args.sortAsc) ? 'asc':'desc';
var sort = [{
field: args.sortCol.field,
@ -196,7 +218,7 @@ my.SlickGrid = Backbone.View.extend({
self.model.query({sort: sort});
});
this.grid.onColumnsReordered.subscribe(function(e, args){
this._slickHandler.subscribe(this.grid.onColumnsReordered, function(e, args){
self.state.set({columnsOrder: _.pluck(self.grid.getColumns(),'id')});
});
@ -212,7 +234,7 @@ my.SlickGrid = Backbone.View.extend({
self.state.set({columnsWidth:columnsWidth});
});
this.grid.onCellChange.subscribe(function (e, args) {
this._slickHandler.subscribe(this.grid.onCellChange, function (e, args) {
// We need to change the model associated value
//
var grid = args.grid;
@ -235,7 +257,12 @@ my.SlickGrid = Backbone.View.extend({
}
return this;
},
},
remove: function () {
this._slickHandler.unsubscribeAll();
Backbone.View.prototype.remove.apply(this, arguments);
},
show: function() {
// If the div is hidden, SlickGrid will calculate wrongly some

View File

@ -4,6 +4,7 @@ this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
// turn off unnecessary logging from VMM Timeline
if (typeof VMM !== 'undefined') {
VMM.debug = false;
@ -27,18 +28,20 @@ my.Timeline = Backbone.View.extend({
initialize: function(options) {
var self = this;
this.el = $(this.el);
this.timeline = new VMM.Timeline();
this.timeline = new VMM.Timeline(this.elementId);
this._timelineIsInitialized = false;
this.model.fields.bind('reset', function() {
this.listenTo(this.model.fields, 'reset', function() {
self._setupTemporalField();
});
this.model.records.bind('all', function() {
this.listenTo(this.model.records, 'all', function() {
self.reloadData();
});
var stateData = _.extend({
startField: null,
endField: null,
// by default timelinejs (and browsers) will parse ambiguous dates in US format (mm/dd/yyyy)
// set to true to interpret dd/dd/dddd as dd/mm/yyyy
nonUSDates: false,
timelineJSOptions: {}
},
options.state
@ -50,7 +53,7 @@ my.Timeline = Backbone.View.extend({
render: function() {
var tmplData = {};
var htmls = Mustache.render(this.template, tmplData);
this.el.html(htmls);
this.$el.html(htmls);
// can only call _initTimeline once view in DOM as Timeline uses $
// internally to look up element
if ($(this.elementId).length > 0) {
@ -66,9 +69,10 @@ my.Timeline = Backbone.View.extend({
},
_initTimeline: function() {
var $timeline = this.el.find(this.elementId);
var data = this._timelineJSON();
this.timeline.init(data, this.elementId, this.state.get("timelineJSOptions"));
var config = this.state.get("timelineJSOptions");
config.id = this.elementId;
this.timeline.init(config, data);
this._timelineIsInitialized = true
},
@ -130,19 +134,33 @@ my.Timeline = Backbone.View.extend({
return out;
},
// convert dates into a format TimelineJS will handle
// TimelineJS does not document this at all so combo of read the code +
// trial and error
// Summary (AFAICt):
// Preferred: [-]yyyy[,mm,dd,hh,mm,ss]
// Supported: mm/dd/yyyy
_parseDate: function(date) {
if (!date) {
return null;
}
var out = date.trim();
var out = $.trim(date);
out = out.replace(/(\d)th/g, '$1');
out = out.replace(/(\d)st/g, '$1');
out = out.trim() ? moment(out) : null;
if (out.toDate() == 'Invalid Date') {
return null;
} else {
return out.toDate();
out = $.trim(out);
if (out.match(/\d\d\d\d-\d\d-\d\d(T.*)?/)) {
out = out.replace(/-/g, ',').replace('T', ',').replace(':',',');
}
if (out.match(/\d\d-\d\d-\d\d.*/)) {
out = out.replace(/-/g, '/');
}
if (this.state.get('nonUSDates')) {
var parts = out.match(/(\d\d)\/(\d\d)\/(\d\d.*)/);
if (parts) {
out = [parts[2], parts[1], parts[3]].join('/');
}
}
return out;
},
_setupTemporalField: function() {

View File

@ -4,6 +4,7 @@ this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
// ## FacetViewer
//
@ -41,9 +42,8 @@ my.FacetViewer = Backbone.View.extend({
},
initialize: function(model) {
_.bindAll(this, 'render');
this.el = $(this.el);
this.model.facets.bind('all', this.render);
this.model.fields.bind('all', this.render);
this.listenTo(this.model.facets, 'all', this.render);
this.listenTo(this.model.fields, 'all', this.render);
this.render();
},
render: function() {
@ -60,17 +60,17 @@ my.FacetViewer = Backbone.View.extend({
return facet;
});
var templated = Mustache.render(this.template, tmplData);
this.el.html(templated);
this.$el.html(templated);
// are there actually any facets to show?
if (this.model.facets.length > 0) {
this.el.show();
this.$el.show();
} else {
this.el.hide();
this.$el.hide();
}
},
onHide: function(e) {
e.preventDefault();
this.el.hide();
this.$el.hide();
},
onFacetFilter: function(e) {
e.preventDefault();

View File

@ -20,7 +20,8 @@ this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
my.Fields = Backbone.View.extend({
className: 'recline-fields-view',
template: ' \
@ -59,13 +60,12 @@ my.Fields = Backbone.View.extend({
initialize: function(model) {
var self = this;
this.el = $(this.el);
_.bindAll(this, 'render');
// TODO: this is quite restrictive in terms of when it is re-run
// e.g. a change in type will not trigger a re-run atm.
// being more liberal (e.g. binding to all) can lead to being called a lot (e.g. for change:width)
this.model.fields.bind('reset', function(action) {
this.listenTo(this.model.fields, 'reset', function(action) {
self.model.fields.each(function(field) {
field.facets.unbind('all', self.render);
field.facets.bind('all', self.render);
@ -74,7 +74,7 @@ my.Fields = Backbone.View.extend({
self.model.getFieldsSummary();
self.render();
});
this.el.find('.collapse').collapse();
this.$el.find('.collapse').collapse();
this.render();
},
render: function() {
@ -88,7 +88,7 @@ my.Fields = Backbone.View.extend({
tmplData.fields.push(out);
});
var templated = Mustache.render(this.template, tmplData);
this.el.html(templated);
this.$el.html(templated);
}
});

View File

@ -4,6 +4,7 @@ this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
my.FilterEditor = Backbone.View.extend({
className: 'recline-filter-editor well',
@ -58,9 +59,9 @@ my.FilterEditor = Backbone.View.extend({
<a class="js-remove-filter" href="#" title="Remove this filter" data-filter-id="{{id}}">&times;</a> \
</legend> \
<label class="control-label" for="">From</label> \
<input type="text" value="{{start}}" name="start" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
<input type="text" value="{{from}}" name="from" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
<label class="control-label" for="">To</label> \
<input type="text" value="{{stop}}" name="stop" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
<input type="text" value="{{to}}" name="to" data-filter-field="{{field}}" data-filter-id="{{id}}" data-filter-type="{{type}}" /> \
</fieldset> \
</div> \
',
@ -88,11 +89,9 @@ my.FilterEditor = Backbone.View.extend({
'submit form.js-add': 'onAddFilter'
},
initialize: function() {
this.el = $(this.el);
_.bindAll(this, 'render');
this.model.fields.bind('all', this.render);
this.model.queryState.bind('change', this.render);
this.model.queryState.bind('change:filters:new-blank', this.render);
this.listenTo(this.model.fields, 'all', this.render);
this.listenTo(this.model.queryState, 'change change:filters:new-blank', this.render);
this.render();
},
render: function() {
@ -108,13 +107,13 @@ my.FilterEditor = Backbone.View.extend({
return Mustache.render(self.filterTemplates[this.type], this);
};
var out = Mustache.render(this.template, tmplData);
this.el.html(out);
this.$el.html(out);
},
onAddFilterShow: function(e) {
e.preventDefault();
var $target = $(e.target);
$target.hide();
this.el.find('form.js-add').show();
this.$el.find('form.js-add').show();
},
onAddFilter: function(e) {
e.preventDefault();

View File

@ -4,6 +4,7 @@ this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
my.Pager = Backbone.View.extend({
className: 'recline-pager',
@ -24,14 +25,13 @@ my.Pager = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
this.el = $(this.el);
this.model.bind('change', this.render);
this.listenTo(this.model, 'change', this.render);
this.render();
},
onFormSubmit: function(e) {
e.preventDefault();
var newFrom = parseInt(this.el.find('input[name="from"]').val());
var newSize = parseInt(this.el.find('input[name="to"]').val()) - newFrom;
var newFrom = parseInt(this.$el.find('input[name="from"]').val());
var newSize = parseInt(this.$el.find('input[name="to"]').val()) - newFrom;
newFrom = Math.max(newFrom, 0);
newSize = Math.max(newSize, 1);
this.model.set({size: newSize, from: newFrom});
@ -52,7 +52,7 @@ my.Pager = Backbone.View.extend({
var tmplData = this.model.toJSON();
tmplData.to = this.model.get('from') + this.model.get('size');
var templated = Mustache.render(this.template, tmplData);
this.el.html(templated);
this.$el.html(templated);
}
});

View File

@ -4,6 +4,7 @@ this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
my.QueryEditor = Backbone.View.extend({
className: 'recline-query-editor',
@ -23,19 +24,18 @@ my.QueryEditor = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render');
this.el = $(this.el);
this.model.bind('change', this.render);
this.listenTo(this.model, 'change', this.render);
this.render();
},
onFormSubmit: function(e) {
e.preventDefault();
var query = this.el.find('.text-query input').val();
var query = this.$el.find('.text-query input').val();
this.model.set({q: query});
},
render: function() {
var tmplData = this.model.toJSON();
var templated = Mustache.render(this.template, tmplData);
this.el.html(templated);
this.$el.html(templated);
}
});

View File

@ -4,6 +4,7 @@ this.recline = this.recline || {};
this.recline.View = this.recline.View || {};
(function($, my) {
"use strict";
my.ValueFilter = Backbone.View.extend({
className: 'recline-filter-editor well',
@ -50,11 +51,9 @@ my.ValueFilter = Backbone.View.extend({
'submit form.js-add': 'onAddFilter'
},
initialize: function() {
this.el = $(this.el);
_.bindAll(this, 'render');
this.model.fields.bind('all', this.render);
this.model.queryState.bind('change', this.render);
this.model.queryState.bind('change:filters:new-blank', this.render);
this.listenTo(this.model.fields, 'all', this.render);
this.listenTo(this.model.queryState, 'change change:filters:new-blank', this.render);
this.render();
},
render: function() {
@ -70,7 +69,7 @@ my.ValueFilter = Backbone.View.extend({
return Mustache.render(self.filterTemplates.term, this);
};
var out = Mustache.render(this.template, tmplData);
this.el.html(out);
this.$el.html(out);
},
updateFilter: function(input) {
var self = this;
@ -84,7 +83,7 @@ my.ValueFilter = Backbone.View.extend({
e.preventDefault();
var $target = $(e.target);
$target.hide();
this.el.find('form.js-add').show();
this.$el.find('form.js-add').show();
},
onAddFilter: function(e) {
e.preventDefault();

View File

@ -64,6 +64,19 @@ test("parseCSV - quotechar", function() {
});
test("parseCSV skipInitialRows", function() {
var csv = '"Jones, Jay",10\n' +
'"Xyz ""ABC"" O\'Brien",11:35\n' +
'"Other, AN",12:35\n';
var array = recline.Backend.CSV.parseCSV(csv, {skipInitialRows: 1});
var exp = [
['Xyz "ABC" O\'Brien', '11:35' ],
['Other, AN', '12:35' ]
];
deepEqual(exp, array);
});
test("serializeCSV - Array", function() {
var csv = [
['Jones, Jay', 10],

View File

@ -1,353 +0,0 @@
(function ($) {
module("Backend ElasticSearch - Wrapper");
test("queryNormalize", function() {
var backend = new recline.Backend.ElasticSearch.Wrapper();
var in_ = new recline.Model.Query();
var out = backend._normalizeQuery(in_);
var exp = {
constant_score: {
query: {
match_all: {}
}
}
};
deepEqual(out, exp);
var in_ = new recline.Model.Query();
in_.set({q: ''});
var out = backend._normalizeQuery(in_);
deepEqual(out, exp);
var in_ = new recline.Model.Query();
in_.attributes.q = 'abc';
var out = backend._normalizeQuery(in_);
equal(out.constant_score.query.query_string.query, 'abc');
var in_ = new recline.Model.Query();
in_.addFilter({
type: 'term',
field: 'xyz',
term: 'XXX'
});
var out = backend._normalizeQuery(in_);
var exp = {
constant_score: {
query: {
match_all: {}
},
filter: {
and: [
{
term: {
xyz: 'xxx'
}
}
]
}
}
};
deepEqual(out, exp);
var in_ = new recline.Model.Query();
in_.addFilter({
type: 'geo_distance',
field: 'xyz'
});
var out = backend._normalizeQuery(in_);
var exp = {
constant_score: {
query: {
match_all: {}
},
filter: {
and: [
{
geo_distance: {
distance: 10,
unit: 'km',
'xyz': { lon: 0, lat: 0 }
}
}
]
}
}
};
deepEqual(out, exp);
});
var mapping_data = {
"note": {
"properties": {
"_created": {
"format": "dateOptionalTime",
"type": "date"
},
"_last_modified": {
"format": "dateOptionalTime",
"type": "date"
},
"end": {
"type": "string"
},
"owner": {
"type": "string"
},
"start": {
"type": "string"
},
"title": {
"type": "string"
}
}
}
};
var sample_data = {
"_shards": {
"failed": 0,
"successful": 5,
"total": 5
},
"hits": {
"hits": [
{
"_id": "u3rpLyuFS3yLNXrtxWkMwg",
"_index": "hypernotes",
"_score": 1.0,
"_source": {
"_created": "2012-02-24T17:53:57.286Z",
"_last_modified": "2012-02-24T17:53:57.286Z",
"owner": "tester",
"title": "Note 1"
},
"_type": "note"
},
{
"_id": "n7JMkFOHSASJCVTXgcpqkA",
"_index": "hypernotes",
"_score": 1.0,
"_source": {
"_created": "2012-02-24T17:53:57.290Z",
"_last_modified": "2012-02-24T17:53:57.290Z",
"owner": "tester",
"title": "Note 3"
},
"_type": "note"
},
{
"_id": "g7UMA55gTJijvsB3dFitzw",
"_index": "hypernotes",
"_score": 1.0,
"_source": {
"_created": "2012-02-24T17:53:57.289Z",
"_last_modified": "2012-02-24T17:53:57.289Z",
"owner": "tester",
"title": "Note 2"
},
"_type": "note"
}
],
"max_score": 1.0,
"total": 3
},
"timed_out": false,
"took": 2
};
test("query", function() {
var backend = new recline.Backend.ElasticSearch.Wrapper('https://localhost:9200/my-es-db/my-es-type');
var stub = sinon.stub($, 'ajax', function(options) {
if (options.url.indexOf('_mapping') != -1) {
return {
done: function(callback) {
callback(mapping_data);
return this;
},
fail: function() {
return this;
}
}
} else {
return {
done: function(callback) {
callback(sample_data);
return this;
},
fail: function() {
}
}
}
});
backend.mapping().done(function(data) {
var fields = _.keys(data.note.properties);
deepEqual(['_created', '_last_modified', 'end', 'owner', 'start', 'title'], fields);
});
backend.query().done(function(queryResult) {
equal(3, queryResult.hits.total);
equal(3, queryResult.hits.hits.length);
equal('Note 1', queryResult.hits.hits[0]._source['title']);
start();
});
$.ajax.restore();
});
// DISABLED - this test requires ElasticSearch to be running locally
// test("write", function() {
// var url = 'http://localhost:9200/recline-test/es-write';
// var backend = new recline.Backend.ElasticSearch.Wrapper(url);
// stop();
//
// var id = parseInt(Math.random()*100000000).toString();
// var rec = {
// id: id,
// title: 'my title'
// };
// var jqxhr = backend.upsert(rec);
// jqxhr.done(function(data) {
// ok(data.ok);
// equal(data._id, id);
// equal(data._type, 'es-write');
// equal(data._version, 1);
//
// // update
// rec.title = 'new title';
// var jqxhr = backend.upsert(rec);
// jqxhr.done(function(data) {
// equal(data._version, 2);
//
// // delete
// var jqxhr = backend.remove(rec.id);
// jqxhr.done(function(data) {
// ok(data.ok);
// rec = null;
//
// // try to get ...
// var jqxhr = backend.get(id);
// jqxhr.done(function(data) {
// // should not be here
// ok(false, 'Should have got 404');
// }).error(function(error) {
// equal(error.status, 404);
// start();
// });
// });
// });
// }).fail(function(error) {
// console.log(error);
// ok(false, 'Basic request failed - is ElasticSearch running locally on port 9200 (required for this test!)');
// start();
// });
// });
// ==================================================
module("Backend ElasticSearch - Recline");
test("query", function() {
var dataset = new recline.Model.Dataset({
url: 'https://localhost:9200/my-es-db/my-es-type',
backend: 'elasticsearch'
});
var stub = sinon.stub($, 'ajax', function(options) {
if (options.url.indexOf('_mapping') != -1) {
return {
done: function(callback) {
callback(mapping_data);
return this;
},
fail: function() {
return this;
}
}
} else {
return {
done: function(callback) {
callback(sample_data);
return this;
},
fail: function() {
}
};
}
});
dataset.fetch().done(function(dataset) {
deepEqual(['_created', '_last_modified', 'end', 'owner', 'start', 'title'], _.pluck(dataset.fields.toJSON(), 'id'));
dataset.query().then(function(recList) {
equal(3, dataset.recordCount);
equal(3, recList.length);
equal('Note 1', recList.models[0].get('title'));
start();
});
});
$.ajax.restore();
});
// DISABLED - this test requires ElasticSearch to be running locally
// test("write", function() {
// var dataset = new recline.Model.Dataset({
// url: 'http://localhost:9200/recline-test/es-write',
// backend: 'elasticsearch'
// });
//
// stop();
//
// var id = parseInt(Math.random()*100000000).toString();
// var rec = new recline.Model.Record({
// id: id,
// title: 'my title'
// });
// dataset.records.add(rec);
// // have to do this explicitly as we not really supporting adding new items atm
// dataset._changes.creates.push(rec.toJSON());
// var jqxhr = dataset.save();
// jqxhr.done(function(data) {
// ok(data.ok);
// equal(data._id, id);
// equal(data._type, 'es-write');
// equal(data._version, 1);
//
// // update
// rec.set({title: 'new title'});
// // again set up by hand ...
// dataset._changes.creates = [];
// dataset._changes.updates.push(rec.toJSON());
// var jqxhr = dataset.save();
// jqxhr.done(function(data) {
// equal(data._version, 2);
//
// // delete
// dataset._changes.updates = 0;
// dataset._changes.deletes.push(rec.toJSON());
// var jqxhr = dataset.save();
// jqxhr.done(function(data) {
// ok(data.ok);
// rec = null;
//
// // try to get ...
// var es = new recline.Backend.ElasticSearch.Wrapper(dataset.get('url'));
// var jqxhr = es.get(id);
// jqxhr.done(function(data) {
// // should not be here
// ok(false, 'Should have got 404');
// }).error(function(error) {
// equal(error.status, 404);
// start();
// });
// });
// });
// }).fail(function(error) {
// console.log(error);
// ok(false, 'Basic request failed - is ElasticSearch running locally on port 9200 (required for this test!)');
// start();
// });
// });
})(this.jQuery);

View File

@ -1,320 +0,0 @@
(function ($) {
module("Backend GDocs");
var sampleGDocsSpreadsheetMetadata = {
feed: {
category: [
{
term: "http://schemas.google.com/spreadsheets/2006#worksheet",
scheme: "http://schemas.google.com/spreadsheets/2006"
}
],
updated: {
$t: "2010-07-13T09:57:28.408Z"
},
xmlns: "http://www.w3.org/2005/Atom",
title: {
$t: "javascript-test",
type: "text"
},
author: [
{
name: {
$t: "okfn.rufus.pollock"
},
email: {
$t: "okfn.rufus.pollock@gmail.com"
}
}
],
openSearch$startIndex: {
$t: "1"
},
xmlns$gs: "http://schemas.google.com/spreadsheets/2006",
xmlns$openSearch: "http://a9.com/-/spec/opensearchrss/1.0/",
entry: [
{
category: [
{
term: "http://schemas.google.com/spreadsheets/2006#worksheet",
scheme: "http://schemas.google.com/spreadsheets/2006"
}
],
updated: {
$t: "2010-07-13T09:57:28.408Z"
},
title: {
$t: "Sheet1",
type: "text"
},
content: {
$t: "Sheet1",
type: "text"
},
link: [
{
href: "https://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/basic",
type: "application/atom+xml",
rel: "http://schemas.google.com/spreadsheets/2006#listfeed"
},
{
href: "https://spreadsheets.google.com/feeds/cells/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/basic",
type: "application/atom+xml",
rel: "http://schemas.google.com/spreadsheets/2006#cellsfeed"
},
{
href: "https://spreadsheets.google.com/tq?key=0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc&sheet=od6&pub=1",
type: "application/atom+xml",
rel: "http://schemas.google.com/visualization/2008#visualizationApi"
},
{
href: "https://spreadsheets.google.com/feeds/worksheets/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/public/basic/od6",
type: "application/atom+xml",
rel: "self"
}
],
id: {
$t: "https://spreadsheets.google.com/feeds/worksheets/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/public/basic/od6"
}
}
],
link: [
{
href: "https://spreadsheets.google.com/pub?key=0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc",
type: "text/html",
rel: "alternate"
},
{
href: "https://spreadsheets.google.com/feeds/worksheets/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/public/basic",
type: "application/atom+xml",
rel: "http://schemas.google.com/g/2005#feed"
},
{
href: "https://spreadsheets.google.com/feeds/worksheets/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/public/basic?alt=json",
type: "application/atom+xml",
rel: "self"
}
],
openSearch$totalResults: {
$t: "1"
},
id: {
$t: "https://spreadsheets.google.com/feeds/worksheets/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/public/basic"
}
},
version: "1.0",
encoding: "UTF-8"
}
var sampleGDocsSpreadsheetData = {
feed: {
category: [
{
term: "http://schemas.google.com/spreadsheets/2006#list",
scheme: "http://schemas.google.com/spreadsheets/2006"
}
],
updated: {
$t: "2010-07-12T18:32:16.200Z"
},
xmlns: "http://www.w3.org/2005/Atom",
xmlns$gsx: "http://schemas.google.com/spreadsheets/2006/extended",
title: {
$t: "Sheet1",
type: "text"
},
author: [
{
name: {
$t: "okfn.rufus.pollock"
},
email: {
$t: "okfn.rufus.pollock@gmail.com"
}
}
],
openSearch$startIndex: {
$t: "1"
},
link: [
{
href: "http://spreadsheets.google.com/pub?key=0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc",
type: "text/html",
rel: "alternate"
},
{
href: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values",
type: "application/atom+xml",
rel: "http://schemas.google.com/g/2005#feed"
},
{
href: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values?alt=json-in-script",
type: "application/atom+xml",
rel: "self"
}
],
xmlns$openSearch: "http://a9.com/-/spec/opensearchrss/1.0/",
entry: [
{
category: [
{
term: "http://schemas.google.com/spreadsheets/2006#list",
scheme: "http://schemas.google.com/spreadsheets/2006"
}
],
updated: {
$t: "2010-07-12T18:32:16.200Z"
},
'gsx$column-2': {
$t: "1"
},
'gsx$column-1': {
$t: "A"
},
title: {
$t: "A",
type: "text"
},
content: {
$t: "column-2: 1",
type: "text"
},
link: [
{
href: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values/cokwr",
type: "application/atom+xml",
rel: "self"
}
],
id: {
$t: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values/cokwr"
}
},
{
category: [
{
term: "http://schemas.google.com/spreadsheets/2006#list",
scheme: "http://schemas.google.com/spreadsheets/2006"
}
],
updated: {
$t: "2010-07-12T18:32:16.200Z"
},
'gsx$column-2': {
$t: "2"
},
'gsx$column-1': {
$t: "b"
},
title: {
$t: "b",
type: "text"
},
content: {
$t: "column-2: 2",
type: "text"
},
link: [
{
href: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values/cpzh4",
type: "application/atom+xml",
rel: "self"
}
],
id: {
$t: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values/cpzh4"
}
},
{
category: [
{
term: "http://schemas.google.com/spreadsheets/2006#list",
scheme: "http://schemas.google.com/spreadsheets/2006"
}
],
updated: {
$t: "2010-07-12T18:32:16.200Z"
},
'gsx$column-2': {
$t: "3"
},
'gsx$column-1': {
$t: "c"
},
title: {
$t: "c",
type: "text"
},
content: {
$t: "column-2: 3",
type: "text"
},
link: [
{
href: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values/cre1l",
type: "application/atom+xml",
rel: "self"
}
],
id: {
$t: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values/cre1l"
}
}
],
openSearch$totalResults: {
$t: "3"
},
id: {
$t: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values"
}
},
version: "1.0",
encoding: "UTF-8"
}
test("GDocs Backend", function() {
var dataset = new recline.Model.Dataset({
url: 'https://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values?alt=json',
backend: 'gdocs'
});
var stub = sinon.stub($, 'getJSON', function(options, cb) {
var spreadsheetUrl = 'spreadsheets.google.com/feeds/worksheets/';
var worksheetUrl = 'spreadsheets.google.com/feeds/list/';
if (options.indexOf(spreadsheetUrl) !== -1) {
cb(sampleGDocsSpreadsheetMetadata)
}
else if(options.indexOf(worksheetUrl) !== -1) {
cb(sampleGDocsSpreadsheetData)
}
});
dataset.fetch().then(function() {
var docList = dataset.records;
deepEqual(['column-2', 'column-1'], _.pluck(dataset.fields.toJSON(), 'id'));
equal(3, docList.length);
equal("A", docList.models[0].get('column-1'));
equal('javascript-test :: Sheet1', dataset.get('title'));
});
$.getJSON.restore();
});
test("GDocs Backend.getUrl", function() {
var key = 'Abc_dajkdkjdafkj';
var gid = 0;
var worksheet = 1;
var url = 'https://docs.google.com/spreadsheet/ccc?key=' + key + '#gid=' + gid
var out = recline.Backend.GDocs.getGDocsAPIUrls(url);
var exp1 = 'https://spreadsheets.google.com/feeds/list/' + key + '/' + worksheet + '/public/values?alt=json'
var exp2 = 'https://spreadsheets.google.com/feeds/worksheets/' + key + '/public/basic?alt=json'
equal(exp1, out.worksheet);
equal(exp2, out.spreadsheet);
var url = 'https://docs.google.com/spreadsheet/ccc?key=' + key;
var out = recline.Backend.GDocs.getGDocsAPIUrls(url);
equal(out.worksheet, exp1);
});
})(this.jQuery);

View File

@ -100,18 +100,19 @@ test('filters', function () {
});
query = new recline.Model.Query();
query.addFilter({type: 'range', field: 'date', start: '2011-01-01', stop: '2011-05-01'});
query.addFilter({type: 'range', field: 'date', from: '2011-01-01', to: '2011-05-01'});
data.query(query.toJSON()).then(function(out) {
equal(out.total, 3);
deepEqual(_.pluck(out.hits, 'date'), ['2011-01-01','2011-02-03','2011-04-05']);
});
query = new recline.Model.Query();
query.addFilter({type: 'range', field: 'z', start: '0', stop: '10'});
query.addFilter({type: 'range', field: 'z', from: '0', to: '10'});
data.query(query.toJSON()).then(function(out) {
equal(out.total, 3);
deepEqual(_.pluck(out.hits, 'z'), [3,6,9]);
});
});
@ -119,35 +120,37 @@ test('filters with nulls', function () {
var data = _wrapData();
query = new recline.Model.Query();
query.addFilter({type: 'range', field: 'z', start: '', stop: null});
query.addFilter({type: 'range', field: 'z', from: '', to: null});
data.query(query.toJSON()).then(function(out) {
equal(out.total, 6);
});
query = new recline.Model.Query();
query.addFilter({type: 'range', field: 'x', start: '', stop: '3'});
query.addFilter({type: 'range', field: 'x', from: '', to: '3'});
data.query(query.toJSON()).then(function(out) {
equal(out.total, 3);
});
query = new recline.Model.Query();
query.addFilter({type: 'range', field: 'x', start: '3', stop: ''});
query.addFilter({type: 'range', field: 'x', from: '3', to: ''});
data.query(query.toJSON()).then(function(out) {
equal(out.total, 4);
});
data.records[5].country = '';
query = new recline.Model.Query();
query.addFilter({type: 'range', field: 'country', start: '', stop: 'Z'});
query.addFilter({type: 'range', field: 'country', from: '', to: 'Z'});
data.query(query.toJSON()).then(function(out) {
equal(out.total, 5);
});
query = new recline.Model.Query();
query.addFilter({type: 'range', field: 'x', start: '', stop: ''});
query.addFilter({type: 'range', field: 'x', from: '', to: ''});
data.query(query.toJSON()).then(function(out) {
equal(out.total, 6);
});
});
test('facet', function () {
@ -305,14 +308,14 @@ test('filters', function () {
});
dataset = makeBackendDataset();
dataset.queryState.addFilter({type: 'range', field: 'date', start: '2011-01-01', stop: '2011-05-01'});
dataset.queryState.addFilter({type: 'range', field: 'date', from: '2011-01-01', to: '2011-05-01'});
dataset.query().then(function() {
equal(dataset.records.length, 3);
deepEqual(dataset.records.pluck('date'), ['2011-01-01','2011-02-03','2011-04-05']);
});
dataset = makeBackendDataset();
dataset.queryState.addFilter({type: 'range', field: 'z', start: '0', stop: '10'});
dataset.queryState.addFilter({type: 'range', field: 'z', from: '0', to: '10'});
dataset.query().then(function() {
equal(dataset.records.length, 3);
deepEqual(dataset.records.pluck('z'), [3,6,9]);

View File

@ -9,8 +9,8 @@
<link rel="stylesheet" href="../css/flot.css" type="text/css" media="screen" />
<script type="text/javascript" src="../vendor/jquery/1.7.1/jquery.js"></script>
<script type="text/javascript" src="../vendor/underscore/1.4.2/underscore.js"></script>
<script type="text/javascript" src="../vendor/backbone/0.9.2/backbone.js"></script>
<script type="text/javascript" src="../vendor/underscore/1.4.4/underscore.js"></script>
<script type="text/javascript" src="../vendor/backbone/1.0.0/backbone.js"></script>
<script type="text/javascript" src="../vendor/jquery-ui-1.8.14.custom.min.js"></script>
<script type="text/javascript" src="../vendor/jquery.flot/0.7/jquery.flot.js"></script>
<script type="text/javascript" src="../vendor/jquery.mustache.js"></script>
@ -18,7 +18,8 @@
<script type="text/javascript" src="../vendor/leaflet/0.3.1/leaflet.js"></script>
<script type="text/javascript" src="qunit/qunit.js"></script>
<script src="sinon/1.1.1/sinon.js"></script>
<script type="text/javascript" src="qunit/qunit-assert-html.js"></script>
<script src="sinon/1.7.1/sinon.js"></script>
<script src="sinon-qunit/1.0.0/sinon-qunit.js"></script>
<!-- Link to the built version of recline -->

View File

@ -4,28 +4,36 @@
<head>
<title>Qunit Tests</title>
<link rel="stylesheet" href="qunit/qunit.css" type="text/css" media="screen" />
<link rel="stylesheet" href="../vendor/timeline/20120520/css/timeline.css" type="text/css" media="screen" />
<link rel="stylesheet" href="../vendor/timeline/css/timeline.css" type="text/css" media="screen" />
<link rel="stylesheet" href="../css/timeline.css" type="text/css" media="screen" />
<style type="text/css">.recline-timeline { height: 400px; }</style>
<link rel="stylesheet" href="../css/flot.css">
<link rel="stylesheet" href="../vendor/leaflet/0.4.4/leaflet.css">
<link rel="stylesheet" href="../vendor/leaflet.markercluster/MarkerCluster.css">
<link rel="stylesheet" href="../vendor/leaflet.markercluster/MarkerCluster.Default.css">
<link rel="stylesheet" href="../css/map.css">
<script type="text/javascript" src="../vendor/jquery/1.7.1/jquery.js"></script>
<script type="text/javascript" src="../vendor/underscore/1.4.2/underscore.js"></script>
<script type="text/javascript" src="../vendor/backbone/0.9.2/backbone.js"></script>
<script type="text/javascript" src="../vendor/moment/1.6.2/moment.js"></script>
<script type="text/javascript" src="../vendor/underscore/1.4.4/underscore.js"></script>
<script type="text/javascript" src="../vendor/backbone/1.0.0/backbone.js"></script>
<script type="text/javascript" src="../vendor/moment/2.0.0/moment.js"></script>
<script type="text/javascript" src="../vendor/mustache/0.5.0-dev/mustache.js"></script>
<script type="text/javascript" src="../vendor/bootstrap/2.0.2/bootstrap.js"></script>
<script type="text/javascript" src="../vendor/flot/jquery.flot.js"></script>
<script type="text/javascript" src="../vendor/flot/jquery.flot.time.js"></script>
<script type="text/javascript" src="../vendor/leaflet/0.4.4/leaflet-src.js"></script>
<script type="text/javascript" src="../vendor/leaflet.markercluster/leaflet.markercluster.js"></script>
<script type="text/javascript" src="../vendor/slickgrid/2.0.1/jquery-ui-1.8.16.custom.min.js"></script>
<script type="text/javascript" src="../vendor/slickgrid/2.0.1/jquery.event.drag-2.0.min.js"></script>
<script type="text/javascript" src="../vendor/slickgrid/2.0.1/slick.grid.min.js"></script>
<script type="text/javascript" src="../vendor/timeline/20120520/js/timeline.js"></script>
<script type="text/javascript" src="../vendor/timeline/js/timeline.js"></script>
<!--[if lte IE 7]>
<script language="javascript" type="text/javascript" src="../vendor/json/json2.js"></script>
<![endif]-->
<script type="text/javascript" src="qunit/qunit.js"></script>
<script src="sinon/1.1.1/sinon.js"></script>
<script type="text/javascript" src="qunit/qunit-assert-html.js"></script>
<script src="sinon/1.7.1/sinon.js"></script>
<script src="sinon-qunit/1.0.0/sinon-qunit.js"></script>
<script type="text/javascript" src="base.js"></script>
@ -34,15 +42,11 @@
<script type="text/javascript" src="../src/model.js"></script>
<script type="text/javascript" src="../src/backend.memory.js"></script>
<script type="text/javascript" src="../src/backend.dataproxy.js"></script>
<script type="text/javascript" src="../src/backend.gdocs.js"></script>
<script type="text/javascript" src="../src/backend.elasticsearch.js"></script>
<script type="text/javascript" src="../src/backend.csv.js"></script>
<script type="text/javascript" src="model.test.js"></script>
<script type="text/javascript" src="backend.memory.test.js"></script>
<script type="text/javascript" src="backend.dataproxy.test.js"></script>
<script type="text/javascript" src="backend.gdocs.test.js"></script>
<script type="text/javascript" src="backend.elasticsearch.test.js"></script>
<script type="text/javascript" src="backend.csv.test.js"></script>
<!-- views and view tests -->

View File

@ -177,6 +177,26 @@ test('Dataset getFieldsSummary', function () {
});
});
test('fetch without and with explicit fields', function () {
var dataset = new recline.Model.Dataset({
backend: 'csv',
data: 'A,B\n1,2\n3,4'
});
dataset.fetch();
equal(dataset.fields.at(0).id, 'A');
equal(dataset.fields.at(0).get('type'), 'string');
var dataset = new recline.Model.Dataset({
fields: [{id: 'X', type: 'number'}, {id: 'Y'}],
backend: 'csv',
data: 'A,B\n1,2\n3,4'
});
dataset.fetch();
equal(dataset.fields.at(0).id, 'X');
equal(dataset.fields.at(0).get('type'), 'number');
equal(dataset.records.at(0).get('X'), 1);
});
test('_normalizeRecordsAndFields', function () {
var data = [
// fields but no records
@ -279,7 +299,7 @@ test('_normalizeRecordsAndFields', function () {
fields: [{id: 'col1'}, {id: 'col2'}],
records: [
{col1: 1, col2: 2},
{col1: 3, col2: 4},
{col1: 3, col2: 4}
]
},
exp: {
@ -327,6 +347,30 @@ test('Query', function () {
deepEqual({terms: {field: 'xyz'}}, query.get('facets')['xyz']);
});
test('Query.addFacet', function () {
var query = new recline.Model.Query();
query.addFacet('xyz', 25);
deepEqual({terms: {field: 'xyz', "size": 25}}, query.get('facets')['xyz']);
});
test('Query.removeFacet', function () {
var query = new recline.Model.Query();
query.addFacet('xyz');
deepEqual({terms: {field: 'xyz'}}, query.get('facets')['xyz']);
query.removeFacet('xyz');
equal(undefined, query.get('facets')['xyz']);
});
test('Query.clearFacets', function () {
var query = new recline.Model.Query();
query.addFacet('abc');
query.addFacet('xyz');
deepEqual({terms: {field: 'xyz'}}, query.get('facets')['xyz']);
deepEqual({terms: {field: 'abc'}}, query.get('facets')['abc']);
query.clearFacets();
deepEqual({}, query.get('facets'));
});
test('Query.addFilter', function () {
var query = new recline.Model.Query();
query.addFilter({type: 'term', field: 'xyz'});
@ -354,4 +398,56 @@ test('Query.addFilter', function () {
deepEqual(exp, query.get('filters')[2]);
});
test('Query.replaceFilter', function () {
var query = new recline.Model.Query();
query.addFilter({type: 'term', field: 'xyz', term: 'one'});
var exp = {
field: 'xyz',
type: 'term',
term: 'one'
};
deepEqual(query.get('filters')[0], exp);
query.replaceFilter({type: 'term', field: 'xyz', term: 'two'});
exp = {
field: 'xyz',
type: 'term',
term: 'two'
};
deepEqual(query.get('filters')[0], exp);
});
test('Query.replaceFilter first filter', function () {
// replaceFilter changes filter order
var query = new recline.Model.Query();
query.addFilter({type: 'term', field: 'abc', term: 'one'});
query.addFilter({type: 'term', field: 'xyz', term: 'two'});
var exp0 = {
field: 'abc',
type: 'term',
term: 'one'
};
deepEqual(query.get('filters')[0], exp0);
var exp1 = {
field: 'xyz',
type: 'term',
term: 'two'
};
deepEqual(query.get('filters')[1], exp1);
var numFilters = query.get('filters').length;
query.replaceFilter({type: 'term', field: 'abc', term: 'three'});
equal(query.get('filters').length, numFilters);
exp0 = {
field: 'abc',
type: 'term',
term: 'three'
};
// deletes original filter and adds replacement to end
deepEqual(query.get('filters')[1], exp0);
deepEqual(query.get('filters')[0], exp1);
});
})(this.jQuery);

View File

@ -0,0 +1,377 @@
/*global QUnit:false */
(function( QUnit, window, undefined ) {
"use strict";
var trim = function( s ) {
if ( !s ) {
return "";
}
return typeof s.trim === "function" ? s.trim() : s.replace( /^\s+|\s+$/g, "" );
};
var normalizeWhitespace = function( s ) {
if ( !s ) {
return "";
}
return trim( s.replace( /\s+/g, " " ) );
};
var dedupeFlatDict = function( dictToDedupe, parentDict ) {
var key, val;
if ( parentDict ) {
for ( key in dictToDedupe ) {
val = dictToDedupe[key];
if ( val && ( val === parentDict[key] ) ) {
delete dictToDedupe[key];
}
}
}
return dictToDedupe;
};
var objectKeys = Object.keys || (function() {
var hasOwn = function( obj, propName ) {
return Object.prototype.hasOwnProperty.call( obj, propName );
};
return function( obj ) {
var keys = [],
key;
for ( key in obj ) {
if ( hasOwn( obj, key ) ) {
keys.push( key );
}
}
return keys;
};
})();
/**
* Calculate based on `currentStyle`/`getComputedStyle` styles instead
*/
var getElementStyles = (function() {
// Memoized
var camelCase = (function() {
var camelCaseFn = (function() {
// Matches dashed string for camelizing
var rmsPrefix = /^-ms-/,
msPrefixFix = "ms-",
rdashAlpha = /-([\da-z])/gi,
camelCaseReplacerFn = function( all, letter ) {
return ( letter + "" ).toUpperCase();
};
return function( s ) {
return s.replace(rmsPrefix, msPrefixFix).replace(rdashAlpha, camelCaseReplacerFn);
};
})();
var camelCaseMemoizer = {};
return function( s ) {
var temp = camelCaseMemoizer[s];
if ( temp ) {
return temp;
}
temp = camelCaseFn( s );
camelCaseMemoizer[s] = temp;
return temp;
};
})();
var styleKeySortingFn = function( a, b ) {
return camelCase( a ) < camelCase( b );
};
return function( elem ) {
var styleCount, i, key,
styles = {},
styleKeys = [],
style = elem.ownerDocument.defaultView ?
elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
elem.currentStyle;
// `getComputedStyle`
if ( style && style.length && style[0] && style[style[0]] ) {
styleCount = style.length;
while ( styleCount-- ) {
styleKeys.push( style[styleCount] );
}
styleKeys.sort( styleKeySortingFn );
for ( i = 0, styleCount = styleKeys.length ; i < styleCount ; i++ ) {
key = styleKeys[i];
if ( key !== "cssText" && typeof style[key] === "string" && style[key] ) {
styles[camelCase( key )] = style[key];
}
}
}
// `currentStyle` support: IE < 9.0, Opera < 10.6
else {
for ( key in style ) {
styleKeys.push( key );
}
styleKeys.sort();
for ( i = 0, styleCount = styleKeys.length ; i < styleCount ; i++ ) {
key = styleKeys[i];
if ( key !== "cssText" && typeof style[key] === "string" && style[key] ) {
styles[key] = style[key];
}
}
}
return styles;
};
})();
var serializeElementNode = function( elementNode, rootNodeStyles ) {
var subNodes, i, len, styles, attrName,
serializedNode = {
NodeType: elementNode.nodeType,
NodeName: elementNode.nodeName.toLowerCase(),
Attributes: {},
ChildNodes: []
};
subNodes = elementNode.attributes;
for ( i = 0, len = subNodes.length ; i < len ; i++ ) {
attrName = subNodes[i].name.toLowerCase();
if ( attrName === "class" ) {
serializedNode.Attributes[attrName] = normalizeWhitespace( subNodes[i].value );
}
else if ( attrName !== "style" ) {
serializedNode.Attributes[attrName] = subNodes[i].value;
}
// Ignore the "style" attribute completely
}
// Only add the style attribute if there is 1+ pertinent rules
styles = dedupeFlatDict( getElementStyles( elementNode ), rootNodeStyles );
if ( styles && objectKeys( styles ).length ) {
serializedNode.Attributes["style"] = styles;
}
subNodes = elementNode.childNodes;
for ( i = 0, len = subNodes.length; i < len; i++ ) {
serializedNode.ChildNodes.push( serializeNode( subNodes[i], rootNodeStyles ) );
}
return serializedNode;
};
var serializeNode = function( node, rootNodeStyles ) {
var serializedNode;
switch (node.nodeType) {
case 1: // Node.ELEMENT_NODE
serializedNode = serializeElementNode( node, rootNodeStyles );
break;
case 3: // Node.TEXT_NODE
serializedNode = {
NodeType: node.nodeType,
NodeName: node.nodeName.toLowerCase(),
NodeValue: node.nodeValue
};
break;
case 4: // Node.CDATA_SECTION_NODE
case 7: // Node.PROCESSING_INSTRUCTION_NODE
case 8: // Node.COMMENT_NODE
serializedNode = {
NodeType: node.nodeType,
NodeName: node.nodeName.toLowerCase(),
NodeValue: trim( node.nodeValue )
};
break;
case 5: // Node.ENTITY_REFERENCE_NODE
case 6: // Node.ENTITY_NODE
case 9: // Node.DOCUMENT_NODE
case 10: // Node.DOCUMENT_TYPE_NODE
case 11: // Node.DOCUMENT_FRAGMENT_NODE
case 12: // Node.NOTATION_NODE
serializedNode = {
NodeType: node.nodeType,
NodeName: node.nodeName
};
break;
case 2: // Node.ATTRIBUTE_NODE
throw new Error( "`node.nodeType` was `Node.ATTRIBUTE_NODE` (2), which is not supported by this method" );
default:
throw new Error( "`node.nodeType` was not recognized: " + node.nodeType );
}
return serializedNode;
};
var serializeHtml = function( html ) {
var scratch = getCleanSlate(),
rootNode = scratch.container(),
rootNodeStyles = getElementStyles( rootNode ),
serializedHtml = [],
kids, i, len;
rootNode.innerHTML = trim( html );
kids = rootNode.childNodes;
for ( i = 0, len = kids.length; i < len; i++ ) {
serializedHtml.push( serializeNode( kids[i], rootNodeStyles ) );
}
scratch.reset();
return serializedHtml;
};
var getCleanSlate = (function() {
var containerElId = "qunit-html-addon-container",
iframeReady = false,
iframeLoaded = function() {
iframeReady = true;
},
iframeReadied = function() {
if (iframe.readyState === "complete" || iframe.readyState === 4) {
iframeReady = true;
}
},
iframeApi,
iframe,
iframeWin,
iframeDoc;
if ( !iframeApi ) {
QUnit.begin(function() {
// Initialize the background iframe!
if ( !iframe || !iframeWin || !iframeDoc ) {
iframe = window.document.createElement( "iframe" );
QUnit.addEvent( iframe, "load", iframeLoaded );
QUnit.addEvent( iframe, "readystatechange", iframeReadied );
iframe.style.position = "absolute";
iframe.style.top = iframe.style.left = "-1000px";
iframe.height = iframe.width = 0;
// `getComputedStyle` behaves inconsistently cross-browser when not attached to a live DOM
window.document.body.appendChild( iframe );
iframeWin = iframe.contentWindow ||
iframe.window ||
iframe.contentDocument && iframe.contentDocument.defaultView ||
iframe.document && ( iframe.document.defaultView || iframe.document.window ) ||
window.frames[( iframe.name || iframe.id )];
iframeDoc = iframeWin && iframeWin.document ||
iframe.contentDocument ||
iframe.document;
var iframeContents = [
"<!DOCTYPE html>",
"<html>",
"<head>",
" <title>QUnit HTML addon iframe</title>",
"</head>",
"<body>",
" <div id=\"" + containerElId + "\"></div>",
" <script type=\"text/javascript\">",
" window.isReady = true;",
" </script>",
"</body>",
"</html>"
].join( "\n" );
iframeDoc.open();
iframeDoc.write( iframeContents );
iframeDoc.close();
// Is ready?
iframeReady = iframeReady || iframeWin.isReady;
}
});
QUnit.done(function() {
if ( iframe && iframe.ownerDocument ) {
iframe.parentNode.removeChild( iframe );
}
iframe = iframeWin = iframeDoc = null;
iframeReady = false;
});
var waitForIframeReady = function( maxTimeout ) {
if ( !iframeReady ) {
if ( !maxTimeout ) {
maxTimeout = 2000; // 2 seconds MAX
}
var startTime = new Date();
while ( !iframeReady && ( ( new Date() - startTime ) < maxTimeout ) ) {
iframeReady = iframeReady || iframeWin.isReady;
}
}
};
iframeApi = {
container: function() {
waitForIframeReady();
if ( iframeReady && iframeDoc ) {
return iframeDoc.getElementById( containerElId );
}
return undefined;
},
reset: function() {
var containerEl = iframeApi.container();
if ( containerEl ) {
containerEl.innerHTML = "";
}
}
};
}
// Actual function signature for `getCleanState`
return function() { return iframeApi; };
})();
QUnit.extend( QUnit.assert, {
/**
* Compare two snippets of HTML for equality after normalization.
*
* @example assert.htmlEqual("<B>Hello, QUnit!</B> ", "<b>Hello, QUnit!</b>", "HTML should be equal");
* @param {String} actual The actual HTML before normalization.
* @param {String} expected The excepted HTML before normalization.
* @param {String} [message] Optional message to display in the results.
*/
htmlEqual: function( actual, expected, message ) {
if ( !message ) {
message = "HTML should be equal";
}
this.deepEqual( serializeHtml( actual ), serializeHtml( expected ), message );
},
/**
* Compare two snippets of HTML for inequality after normalization.
*
* @example assert.notHtmlEqual("<b>Hello, <i>QUnit!</i></b>", "<b>Hello, QUnit!</b>", "HTML should not be equal");
* @param {String} actual The actual HTML before normalization.
* @param {String} expected The excepted HTML before normalization.
* @param {String} [message] Optional message to display in the results.
*/
notHtmlEqual: function( actual, expected, message ) {
if ( !message ) {
message = "HTML should not be equal";
}
this.notDeepEqual( serializeHtml( actual ), serializeHtml( expected ), message );
},
/**
* @private
* Normalize and serialize an HTML snippet. Primarily only exposed for unit testing purposes.
*
* @example assert._serializeHtml('<b style="color:red;">Test</b>');
* @param {String} html The HTML snippet to normalize and serialize.
* @returns {Object[]} The normalized and serialized form of the HTML snippet.
*/
_serializeHtml: serializeHtml
});
})( QUnit, this );

View File

@ -1,11 +1,11 @@
/**
* QUnit v1.6.0 - A JavaScript Unit Testing Framework
* QUnit v1.11.0 - A JavaScript Unit Testing Framework
*
* http://docs.jquery.com/QUnit
* http://qunitjs.com
*
* Copyright (c) 2012 John Resig, Jörn Zaefferer
* Dual licensed under the MIT (MIT-LICENSE.txt)
* or GPL (GPL-LICENSE.txt) licenses.
* Copyright 2012 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
/** Font Family and Sizes */
@ -20,7 +20,7 @@
/** Resets */
#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0;
padding: 0;
}
@ -38,10 +38,10 @@
line-height: 1em;
font-weight: normal;
border-radius: 15px 15px 0 0;
-moz-border-radius: 15px 15px 0 0;
-webkit-border-top-right-radius: 15px;
-webkit-border-top-left-radius: 15px;
border-radius: 5px 5px 0 0;
-moz-border-radius: 5px 5px 0 0;
-webkit-border-top-right-radius: 5px;
-webkit-border-top-left-radius: 5px;
}
#qunit-header a {
@ -54,9 +54,9 @@
color: #fff;
}
#qunit-header label {
#qunit-testrunner-toolbar label {
display: inline-block;
padding-left: 0.5em;
padding: 0 .5em 0 .1em;
}
#qunit-banner {
@ -67,6 +67,7 @@
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
overflow: hidden;
}
#qunit-userAgent {
@ -76,6 +77,9 @@
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
#qunit-modulefilter-container {
float: right;
}
/** Tests: Pass/Fail */
@ -107,19 +111,24 @@
color: #000;
}
#qunit-tests ol {
#qunit-tests li .runtime {
float: right;
font-size: smaller;
}
.qunit-assert-list {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
box-shadow: inset 0px 2px 13px #999;
-moz-box-shadow: inset 0px 2px 13px #999;
-webkit-box-shadow: inset 0px 2px 13px #999;
.qunit-collapsed {
display: none;
}
#qunit-tests table {
@ -162,8 +171,7 @@
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
margin: 0.5em;
padding: 0.4em 0.5em 0.4em 0.5em;
padding: 5px;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
@ -172,9 +180,9 @@
/*** Passing Styles */
#qunit-tests li li.pass {
color: #5E740B;
color: #3c510c;
background-color: #fff;
border-left: 26px solid #C6E746;
border-left: 10px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
@ -190,15 +198,15 @@
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 26px solid #EE5757;
border-left: 10px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 15px 15px;
-moz-border-radius: 0 0 15px 15px;
-webkit-border-bottom-right-radius: 15px;
-webkit-border-bottom-left-radius: 15px;
border-radius: 0 0 5px 5px;
-moz-border-radius: 0 0 5px 5px;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }

File diff suppressed because it is too large Load Diff

139
test/qunit/runner.js Normal file
View File

@ -0,0 +1,139 @@
/*
* QtWebKit-powered headless test runner using PhantomJS
*
* PhantomJS binaries: http://phantomjs.org/download.html
* Requires PhantomJS 1.6+ (1.7+ recommended)
*
* Run with:
* phantomjs runner.js [url-of-your-qunit-testsuite]
*
* e.g.
* phantomjs runner.js http://localhost/qunit/test/index.html
*/
/*global phantom:false, require:false, console:false, window:false, QUnit:false */
(function() {
'use strict';
var url, page, timeout,
args = require('system').args;
// arg[0]: scriptName, args[1...]: arguments
if (args.length < 2 || args.length > 3) {
console.error('Usage:\n phantomjs runner.js [url-of-your-qunit-testsuite] [timeout-in-seconds]');
phantom.exit(1);
}
url = args[1];
page = require('webpage').create();
if (args[2] !== undefined) {
timeout = parseInt(args[2], 10);
}
// Route `console.log()` calls from within the Page context to the main Phantom context (i.e. current `this`)
page.onConsoleMessage = function(msg) {
console.log(msg);
};
page.onInitialized = function() {
page.evaluate(addLogging);
};
page.onCallback = function(message) {
var result,
failed;
if (message) {
if (message.name === 'QUnit.done') {
result = message.data;
failed = !result || result.failed;
phantom.exit(failed ? 1 : 0);
}
}
};
page.open(url, function(status) {
if (status !== 'success') {
console.error('Unable to access network: ' + status);
phantom.exit(1);
} else {
// Cannot do this verification with the 'DOMContentLoaded' handler because it
// will be too late to attach it if a page does not have any script tags.
var qunitMissing = page.evaluate(function() { return (typeof QUnit === 'undefined' || !QUnit); });
if (qunitMissing) {
console.error('The `QUnit` object is not present on this page.');
phantom.exit(1);
}
// Set a timeout on the test running, otherwise tests with async problems will hang forever
if (typeof timeout === 'number') {
setTimeout(function() {
console.error('The specified timeout of ' + timeout + ' seconds has expired. Aborting...');
phantom.exit(1);
}, timeout * 1000);
}
// Do nothing... the callback mechanism will handle everything!
}
});
function addLogging() {
window.document.addEventListener('DOMContentLoaded', function() {
var currentTestAssertions = [];
QUnit.log(function(details) {
var response;
// Ignore passing assertions
if (details.result) {
return;
}
response = details.message || '';
if (typeof details.expected !== 'undefined') {
if (response) {
response += ', ';
}
response += 'expected: ' + details.expected + ', but was: ' + details.actual;
}
if (details.source) {
response += "\n" + details.source;
}
currentTestAssertions.push('Failed assertion: ' + response);
});
QUnit.testDone(function(result) {
var i,
len,
name = result.module + ': ' + result.name;
if (result.failed) {
console.log('Test failed: ' + name);
for (i = 0, len = currentTestAssertions.length; i < len; i++) {
console.log(' ' + currentTestAssertions[i]);
}
}
currentTestAssertions.length = 0;
});
QUnit.done(function(result) {
console.log('Took ' + result.runtime + 'ms to run ' + result.total + ' tests. ' + result.passed + ' passed, ' + result.failed + ' failed.');
if (typeof window.callPhantom === 'function') {
window.callPhantom({
'name': 'QUnit.done',
'data': result
});
}
});
}, false);
}
})();

File diff suppressed because it is too large Load Diff

4299
test/sinon/1.7.1/sinon.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -29,8 +29,8 @@ test('initialize', function () {
// check we have updated editor with state info
equal(view.elSidebar.find('.editor-type select').val(), 'lines');
equal(view.elSidebar.find('.editor-group select').val(), 'x');
var out = _.map(view.elSidebar.find('.editor-series select'), function($el) {
return $($el).val();
var out = _.map(view.elSidebar.find('.editor-series select'), function(el) {
return $(el).val();
});
deepEqual(out, ['y', 'z']);
@ -48,7 +48,9 @@ test('dates in graph view', function () {
'series': ['y', 'z']
}
});
view.render();
$('.fixtures').append(view.el);
view.redraw();
view.remove();
});

View File

@ -110,10 +110,14 @@ test('GeoJSON geom field', function () {
test('_getGeometryFromRecord non-GeoJSON', function () {
var test = [
[{ lon: 47, lat: 53}, [47,53]],
[{ lon: -47, lat: 53}, [-47,53]],
["53.3,47.32", [47.32, 53.3]],
["53.3, 47.32", [47.32, 53.3]],
["(53.3,47.32)", [47.32, 53.3]],
[[53.3,47.32], [53.3, 47.32]]
[[53.3,47.32], [53.3, 47.32]],
["53.3 N, 113.5 W", [-113.5, 53.3]],
["53° 18' N, 113° 30' W", [-113.5, 53.3 ]],
["22°4590″S, 43°1545″W", [-43.2625, -22.775]]
];
var view = new recline.View.Map({
model: new recline.Model.Dataset({
@ -130,7 +134,7 @@ test('_getGeometryFromRecord non-GeoJSON', function () {
});
});
test('many markers', function () {
test('many markers and clustering', function () {
var data = [];
for (var i = 0; i<1000; i++) {
data.push({ id: i, lon: 13+3*i, lat: 52+i/10});
@ -150,7 +154,13 @@ test('many markers', function () {
dataset.query();
// this whole test looks a bit odd now
// we used to turn on clustering automatically at a certain level but we do not any more
equal(view.state.get('cluster'), false);
view.state.set({cluster: true});
equal(view.state.get('cluster'), true);
view.remove();
});
@ -162,13 +172,13 @@ test('Popup', function () {
$('.fixtures').append(view.el);
view.render();
var marker = view.el.find('.leaflet-marker-icon').first();
var marker = view.$el.find('.leaflet-marker-icon').first();
assertPresent(marker);
_.values(view.features._layers)[0].fire('click');
var popup = view.el.find('.leaflet-popup-content');
var popup = view.$el.find('.leaflet-popup-content');
assertPresent(popup);
@ -183,7 +193,7 @@ test('Popup', function () {
view.remove();
});
test('Popup - Custom', function () {
test('Popup - Custom', function (assert) {
var dataset = GeoJSONFixture.getDataset();
var view = new recline.View.Map({
model: dataset
@ -195,14 +205,14 @@ test('Popup - Custom', function () {
};
view.render();
var marker = view.el.find('.leaflet-marker-icon').first();
var marker = view.$el.find('.leaflet-marker-icon').first();
_.values(view.features._layers)[0].fire('click');
var popup = view.el.find('.leaflet-popup-content');
var popup = view.$el.find('.leaflet-popup-content');
assertPresent(popup);
var text = popup.html();
ok((text.indexOf('<h3>1</h3>y: 2') != -1))
assert.htmlEqual(text, '<h3>1</h3>y: 2');
view.remove();
});
@ -213,7 +223,7 @@ test('geoJsonLayerOptions', function () {
model: dataset
});
$('.fixtures').append(view.el);
view.geoJsonLayerOptions.point
view.geoJsonLayerOptions.point
view.geoJsonLayerOptions.pointToLayer = function(feature, latlng) {
var marker = new L.CircleMarker(latlng, { radius: 8 } );
marker.bindPopup(feature.properties.popupContent);

View File

@ -12,7 +12,7 @@ test('basic explorer functionality', function () {
});
var $explorer = $el.find('.recline-data-explorer');
equal($explorer.length, 1);
$el.remove();
explorer.remove();
});
test('get State', function () {
@ -34,7 +34,7 @@ test('get State', function () {
equal(state.get('backend'), 'memory');
equal(state.get('dataset').url, 'xyz');
ok(state.get('url') === url);
$el.remove();
explorer.remove();
});
test('initialize state', function () {
@ -60,16 +60,16 @@ test('initialize state', function () {
ok(explorer.state.get('currentView'), 'graph');
// check the correct view is visible
var css = explorer.el.find('.navigation a[data-view="graph"]').attr('class').split(' ');
var css = explorer.$el.find('.navigation a[data-view="graph"]').attr('class').split(' ');
ok(_.contains(css, 'active'), css);
var css = explorer.el.find('.navigation a[data-view="grid"]').attr('class').split(' ');
var css = explorer.$el.find('.navigation a[data-view="grid"]').attr('class').split(' ');
ok(!(_.contains(css, 'active')), css);
// check pass through of view config
deepEqual(explorer.state.get('view-grid')['hiddenFields'], ['x']);
equal(explorer.state.get('view-map')['lonField'], 'lon1');
$el.remove();
explorer.remove();
});
test('restore (from serialized state)', function() {
@ -83,6 +83,9 @@ test('restore (from serialized state)', function() {
var out = explorerNew.state.toJSON();
equal(out.backend, state.backend);
explorer.remove();
explorerNew.remove();
var dataset = new recline.Model.Dataset({
url: 'http://data.london.gov.uk/datafiles/transport/tfl_passengers.csv',
format: 'csv',
@ -95,6 +98,9 @@ test('restore (from serialized state)', function() {
var explorerNew = recline.View.MultiView.restore(state);
equal(explorerNew.model.get('backend'), 'dataproxy');
equal(explorerNew.model.get('format'), 'csv');
explorer.remove();
explorerNew.remove();
});
})(this.jQuery);

View File

@ -83,7 +83,7 @@ test('editable', function () {
$('.fixtures .test-datatable').append(view.el);
view.render();
view.grid.init();
view.show();
var new_item = {lon: "foo", id: 1, z: 23, date: "12", y: 3, country: 'FR'};
@ -92,13 +92,15 @@ test('editable', function () {
});
// Be sure a cell change triggers a change of the model
e = new Slick.EventData();
return view.grid.onCellChange.notify({
row: 1,
cell: 0,
item: new_item,
grid: view.grid
}, e, view.grid);
e = new Slick.EventData();
view.grid.onCellChange.notify({
row: 1,
cell: 0,
item: new_item,
grid: view.grid
}, e, view.grid);
view.remove();
});
test('update', function() {
@ -124,9 +126,11 @@ test('update', function() {
// Change the model at row 1
dataset.records.at(1).set('z', zbefore + 1);
equal( zbefore + 1, view.grid.getData().getItem(1)['z']);
view.remove();
});
test('renderers', function () {
test('renderers', function (assert) {
var dataset = Fixture.getDataset();
dataset.fields.get('country').renderer = function(val, field, doc){
@ -148,7 +152,7 @@ test('renderers', function () {
view.grid.init();
equal($(view.grid.getCellNode(0,view.grid.getColumnIndex('country'))).text(),'Country: DE');
equal($(view.grid.getCellNode(0,view.grid.getColumnIndex('country'))).html(),'<a href="abc">Country: DE</a>');
assert.htmlEqual($(view.grid.getCellNode(0,view.grid.getColumnIndex('country'))).html(),'<a href="abc">Country: DE</a>');
equal($(view.grid.getCellNode(0,view.grid.getColumnIndex('computed'))).text(),'10');
view.remove();
});

View File

@ -19,13 +19,13 @@ test('extract dates and timelineJSON', function () {
'headline': '',
'date': [
{
'startDate': new Date('2012-03-20'),
'startDate': '2012,03,20',
'endDate': null,
'headline': '1',
'text': '<div class="recline-record-summary"><div class="Date"><strong>Date</strong>: 2012-03-20</div><div class="title"><strong>title</strong>: 1</div></div>'
},
{
'startDate': new Date('2012-03-25'),
'startDate': '2012,03,25',
'endDate': null,
'headline': '2',
'text': '<div class="recline-record-summary"><div class="Date"><strong>Date</strong>: 2012-03-25</div><div class="title"><strong>title</strong>: 2</div></div>'
@ -37,17 +37,22 @@ test('extract dates and timelineJSON', function () {
});
test('render etc', function () {
var dataset = Fixture.getDataset();
var dataset = new recline.Model.Dataset({
records: [
{'Date': '-10000', 'title': 'first'},
{'Date': '2012-03-20', 'title': 'second'},
{'Date': '2012-03-25', 'title': 'third'}
]
});
var view = new recline.View.Timeline({
model: dataset
});
view.render();
$('.fixtures').append(view.el);
view._initTimeline();
assertPresent('.vmm-timeline', view.el);
view.render();
assertPresent('.vco-timeline', view.el);
assertPresent('.timenav', view.el);
assertPresent('.timenav', view.el);
equal(view.el.find('.marker.active h4').text(), '2011');
equal(view.$el.find('.marker.active h3').text(), 'first');
view.remove();
});
@ -57,18 +62,22 @@ test('_parseDate', function () {
model: dataset
});
var testData = [
[ '1st August 1914', '1914-08-01T00:00:00.000Z' ],
[ '1 August 1914', '1914-08-01T00:00:00.000Z' ],
[ 'August 1st 1914', '1914-08-01T00:00:00.000Z' ],
[ '1914-08-01', '1914-08-01T00:00:00.000Z' ],
[ '1914-08-01T08:00', '1914-08-01T08:00:00.000Z' ],
[ 'afdaf afdaf', null ],
// [ '1st August 1914', '1914-08-01T00:00:00.000Z' ],
// [ '1 August 1914', '1914-08-01T00:00:00.000Z' ],
// [ 'August 1st 1914', '1914-08-01T00:00:00.000Z' ],
[ '1914-08-01', '1914,08,01' ],
[ '1914-08-01T08:00', '1914,08,01,08,00' ],
[ '03-20-1914', '03/20/1914' ],
[ '20/03/1914', '20/03/1914' ],
[ '-10000', '-10000' ],
[ null, null ]
];
_.each(testData, function(item) {
var out = view._parseDate(item[0]);
if (out) out = out.toISOString();
equal(out, item[1]);
});
view.state.set({'nonUSDates': true});
var out = view._parseDate('20/03/1914');
equal(out, '03/20/1914');
});

View File

@ -7,10 +7,10 @@ test('basics', function () {
});
$('.fixtures').append(view.el);
assertPresent('.js-add-filter', view.elSidebar);
var $addForm = view.el.find('form.js-add');
var $addForm = view.$el.find('form.js-add');
ok(!$addForm.is(":visible"));
view.el.find('.js-add-filter').click();
ok(!view.el.find('.js-add-filter').is(":visible"));
view.$el.find('.js-add-filter').click();
ok(!view.$el.find('.js-add-filter').is(":visible"));
ok($addForm.is(":visible"));
// submit the form
@ -19,7 +19,7 @@ test('basics', function () {
// now check we have new filter
ok(!$addForm.is(":visible"));
$editForm = view.el.find('form.js-edit');
$editForm = view.$el.find('form.js-edit');
equal($editForm.find('.filter-term').length, 1)
equal(dataset.queryState.attributes.filters[0].field, 'country');
@ -30,30 +30,30 @@ test('basics', function () {
equal(dataset.records.length, 3);
// now set a second range filter ...
view.el.find('.js-add-filter').click();
var $addForm = view.el.find('form.js-add');
view.$el.find('.js-add-filter').click();
var $addForm = view.$el.find('form.js-add');
$addForm.find('select.fields').val('x');
$addForm.find('select.filterType').val('range');
$addForm.submit();
$editForm = view.el.find('form.js-edit');
$editForm = view.$el.find('form.js-edit');
$editForm.find('.filter-range input').first().val('2');
$editForm.find('.filter-range input').last().val('4');
$editForm.submit();
equal(dataset.queryState.attributes.filters[0].term, 'UK');
equal(dataset.queryState.attributes.filters[1].start, 2);
equal(dataset.queryState.attributes.filters[1].from, 2);
equal(dataset.records.length, 2);
// now remove filter
$editForm = view.el.find('form.js-edit');
$editForm = view.$el.find('form.js-edit');
$editForm.find('.js-remove-filter').last().click();
$editForm = view.el.find('form.js-edit');
$editForm = view.$el.find('form.js-edit');
equal($editForm.find('.filter').length, 1)
equal(dataset.records.length, 3);
$editForm = view.el.find('form.js-edit');
$editForm = view.$el.find('form.js-edit');
$editForm.find('.js-remove-filter').last().click();
$editForm = view.el.find('form.js-edit');
$editForm = view.$el.find('form.js-edit');
equal($editForm.find('.filter').length, 0)
equal(dataset.records.length, 6);
@ -68,18 +68,18 @@ test('add 2 filters of same type', function () {
$('.fixtures').append(view.el);
// add 2 term filters
var $addForm = view.el.find('form.js-add');
view.el.find('.js-add-filter').click();
var $addForm = view.$el.find('form.js-add');
view.$el.find('.js-add-filter').click();
$addForm.find('select.fields').val('country');
$addForm.submit();
var $addForm = view.el.find('form.js-add');
view.el.find('.js-add-filter').click();
var $addForm = view.$el.find('form.js-add');
view.$el.find('.js-add-filter').click();
$addForm.find('select.fields').val('id');
$addForm.submit();
var fields = [];
view.el.find('form.js-edit .filter-term input').each(function(idx, item) {
view.$el.find('form.js-edit .filter-term input').each(function(idx, item) {
fields.push($(item).attr('data-filter-field'));
});
deepEqual(fields, ['country', 'id']);
@ -94,14 +94,14 @@ test('geo_distance', function () {
});
$('.fixtures').append(view.el);
var $addForm = view.el.find('form.js-add');
var $addForm = view.$el.find('form.js-add');
// submit the form
$addForm.find('select.filterType').val('geo_distance');
$addForm.find('select.fields').val('lon');
$addForm.submit();
// now check we have new filter
$editForm = view.el.find('form.js-edit');
$editForm = view.$el.find('form.js-edit');
equal($editForm.find('.filter-geo_distance').length, 1)
deepEqual(_.sortBy(_.keys(dataset.queryState.attributes.filters[0]),_.identity),
["distance", "field", "point", "type", "unit"]);

View File

@ -7,10 +7,10 @@ test('basics', function () {
});
$('.fixtures').append(view.el);
assertPresent('.js-add-filter', view.elSidebar);
var $addForm = view.el.find('form.js-add');
var $addForm = view.$el.find('form.js-add');
ok(!$addForm.is(":visible"));
view.el.find('.js-add-filter').click();
ok(!view.el.find('.js-add-filter').is(":visible"));
view.$el.find('.js-add-filter').click();
ok(!view.$el.find('.js-add-filter').is(":visible"));
ok($addForm.is(":visible"));
// submit the form
@ -19,7 +19,7 @@ test('basics', function () {
// now check we have new filter
ok(!$addForm.is(":visible"));
$editForm = view.el.find('form.js-edit');
$editForm = view.$el.find('form.js-edit');
equal($editForm.find('.filter-term').length, 1);
equal(dataset.queryState.attributes.filters[0].field, 'country');
@ -30,9 +30,9 @@ test('basics', function () {
equal(dataset.records.length, 3);
// now remove filter
$editForm = view.el.find('form.js-edit');
$editForm = view.$el.find('form.js-edit');
$editForm.find('.js-remove-filter').last().click();
$editForm = view.el.find('form.js-edit');
$editForm = view.$el.find('form.js-edit');
equal($editForm.find('.filter').length, 0);
equal(dataset.records.length, 6);
@ -47,18 +47,19 @@ test('add 2 filters', function () {
$('.fixtures').append(view.el);
// add 2 term filters
var $addForm = view.el.find('form.js-add');
view.el.find('.js-add-filter').click();
var $addForm = view.$el.find('form.js-add');
view.$el.find('.js-add-filter').click();
$addForm.find('select.fields').val('country');
$addForm.submit();
$addForm = view.el.find('form.js-add');
view.el.find('.js-add-filter').click();
$addForm = view.$el.find('form.js-add');
view.$el.find('.js-add-filter').click();
$addForm.find('select.fields').val('id');
$addForm.submit();
var fields = [];
view.el.find('form.js-edit .filter-term input').each(function(idx, item) {
view.$el.find('form.js-edit .filter-term input').each(function(idx, item) {
fields.push($(item).attr('data-filter-field'));
});
deepEqual(fields, ['country', 'id']);

View File

@ -1,38 +0,0 @@
// Backbone.js 0.9.2
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// http://backbonejs.org
(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks=
{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g=
z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent=
{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null==
b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent:
b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)};
a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error,
h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t();
return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending=
{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length||
!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator);
this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c<d;c++){if(!(e=a[c]=this._prepareModel(a[c],b)))throw Error("Can't add an invalid model to a collection");g=e.cid;i=e.id;j[g]||this._byCid[g]||null!=i&&(k[i]||this._byId[i])?
l.push(c):j[g]=k[i]=e}for(c=l.length;c--;)a.splice(l[c],1);c=0;for(d=a.length;c<d;c++)(e=a[c]).on("all",this._onModelEvent,this),this._byCid[e.cid]=e,null!=e.id&&(this._byId[e.id]=e);this.length+=d;A.apply(this.models,[null!=b.at?b.at:this.models.length,0].concat(a));this.comparator&&this.sort({silent:!0});if(b.silent)return this;c=0;for(d=this.models.length;c<d;c++)if(j[(e=this.models[c]).cid])b.index=c,e.trigger("add",e,this,b);return this},remove:function(a,b){var c,d,e,g;b||(b={});a=f.isArray(a)?
a.slice():[a];c=0;for(d=a.length;c<d;c++)if(g=this.getByCid(a[c])||this.get(a[c]))delete this._byId[g.id],delete this._byCid[g.cid],e=this.indexOf(g),this.models.splice(e,1),this.length--,b.silent||(b.index=e,g.trigger("remove",g,this,b)),this._removeReference(g);return this},push:function(a,b){a=this._prepareModel(a,b);this.add(a,b);return a},pop:function(a){var b=this.at(this.length-1);this.remove(b,a);return b},unshift:function(a,b){a=this._prepareModel(a,b);this.add(a,f.extend({at:0},b));return a},
shift:function(a){var b=this.at(0);this.remove(b,a);return b},get:function(a){return null==a?void 0:this._byId[null!=a.id?a.id:a]},getByCid:function(a){return a&&this._byCid[a.cid||a]},at:function(a){return this.models[a]},where:function(a){return f.isEmpty(a)?[]:this.filter(function(b){for(var c in a)if(a[c]!==b.get(c))return!1;return!0})},sort:function(a){a||(a={});if(!this.comparator)throw Error("Cannot sort a set without a comparator");var b=f.bind(this.comparator,this);1==this.comparator.length?
this.models=this.sortBy(b):this.models.sort(b);a.silent||this.trigger("reset",this,a);return this},pluck:function(a){return f.map(this.models,function(b){return b.get(a)})},reset:function(a,b){a||(a=[]);b||(b={});for(var c=0,d=this.models.length;c<d;c++)this._removeReference(this.models[c]);this._reset();this.add(a,f.extend({silent:!0},b));b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a=a?f.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=this,c=a.success;a.success=function(d,
e,f){b[a.add?"add":"reset"](b.parse(d,f),a);c&&c(b,d)};a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},create:function(a,b){var c=this,b=b?f.clone(b):{},a=this._prepareModel(a,b);if(!a)return!1;b.wait||c.add(a,b);var d=b.success;b.success=function(e,f){b.wait&&c.add(e,b);d?d(e,f):e.trigger("sync",a,f,b)};a.save(null,b);return a},parse:function(a){return a},chain:function(){return f(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId=
{};this._byCid={}},_prepareModel:function(a,b){b||(b={});a instanceof o?a.collection||(a.collection=this):(b.collection=this,a=new this.model(a,b),a._validate(a.attributes,b)||(a=!1));return a},_removeReference:function(a){this==a.collection&&delete a.collection;a.off("all",this._onModelEvent,this)},_onModelEvent:function(a,b,c,d){("add"==a||"remove"==a)&&c!=this||("destroy"==a&&this.remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],this._byId[b.id]=b),this.trigger.apply(this,
arguments))}});f.each("forEach,each,map,reduce,reduceRight,find,detect,filter,select,reject,every,all,some,any,include,contains,invoke,max,min,sortBy,sortedIndex,toArray,size,first,initial,rest,last,without,indexOf,shuffle,lastIndexOf,isEmpty,groupBy".split(","),function(a){r.prototype[a]=function(){return f[a].apply(f,[this.models].concat(f.toArray(arguments)))}});var u=g.Router=function(a){a||(a={});a.routes&&(this.routes=a.routes);this._bindRoutes();this.initialize.apply(this,arguments)},B=/:\w+/g,
C=/\*\w+/g,D=/[-[\]{}()+?.,\\^$|#\s]/g;f.extend(u.prototype,k,{initialize:function(){},route:function(a,b,c){g.history||(g.history=new m);f.isRegExp(a)||(a=this._routeToRegExp(a));c||(c=this[b]);g.history.route(a,f.bind(function(d){d=this._extractParameters(a,d);c&&c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d));g.history.trigger("route",this,b,d)},this));return this},navigate:function(a,b){g.history.navigate(a,b)},_bindRoutes:function(){if(this.routes){var a=[],b;for(b in this.routes)a.unshift([b,
this.routes[b]]);b=0;for(var c=a.length;b<c;b++)this.route(a[b][0],a[b][1],this[a[b][1]])}},_routeToRegExp:function(a){a=a.replace(D,"\\$&").replace(B,"([^/]+)").replace(C,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});var m=g.History=function(){this.handlers=[];f.bindAll(this,"checkUrl")},s=/^[#\/]/,E=/msie [\w.]+/;m.started=!1;f.extend(m.prototype,k,{interval:50,getHash:function(a){return(a=(a?a.location:window.location).href.match(/#(.*)$/))?a[1]:
""},getFragment:function(a,b){if(null==a)if(this._hasPushState||b){var a=window.location.pathname,c=window.location.search;c&&(a+=c)}else a=this.getHash();a.indexOf(this.options.root)||(a=a.substr(this.options.root.length));return a.replace(s,"")},start:function(a){if(m.started)throw Error("Backbone.history has already been started");m.started=!0;this.options=f.extend({},{root:"/"},this.options,a);this._wantsHashChange=!1!==this.options.hashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=
!(!this.options.pushState||!window.history||!window.history.pushState);var a=this.getFragment(),b=document.documentMode;if(b=E.exec(navigator.userAgent.toLowerCase())&&(!b||7>=b))this.iframe=i('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a);this._hasPushState?i(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!b?i(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,
this.interval));this.fragment=a;a=window.location;b=a.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&b&&a.hash&&(this.fragment=this.getHash().replace(s,""),window.history.replaceState({},document.title,a.protocol+"//"+a.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},
stop:function(){i(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);m.started=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.getHash(this.iframe)));if(a==this.fragment)return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(a){var b=this.fragment=this.getFragment(a);return f.any(this.handlers,
function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!m.started)return!1;if(!b||!0===b)b={trigger:b};var c=(a||"").replace(s,"");this.fragment!=c&&(this._hasPushState?(0!=c.indexOf(this.options.root)&&(c=this.options.root+c),this.fragment=c,window.history[b.replace?"replaceState":"pushState"]({},document.title,c)):this._wantsHashChange?(this.fragment=c,this._updateHash(window.location,c,b.replace),this.iframe&&c!=this.getFragment(this.getHash(this.iframe))&&(b.replace||
this.iframe.document.open().close(),this._updateHash(this.iframe.location,c,b.replace))):window.location.assign(this.options.root+a),b.trigger&&this.loadUrl(a))},_updateHash:function(a,b,c){c?a.replace(a.toString().replace(/(javascript:|#).*$/,"")+"#"+b):a.hash=b}});var v=g.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()},F=/^(\S+)\s*(.*)$/,w="model,collection,el,id,attributes,className,tagName".split(",");
f.extend(v.prototype,k,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();return this},make:function(a,b,c){a=document.createElement(a);b&&i(a).attr(b);c&&i(a).html(c);return a},setElement:function(a,b){this.$el&&this.undelegateEvents();this.$el=a instanceof i?a:i(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=n(this,"events"))){this.undelegateEvents();
for(var b in a){var c=a[b];f.isFunction(c)||(c=this[a[b]]);if(!c)throw Error('Method "'+a[b]+'" does not exist');var d=b.match(F),e=d[1],d=d[2],c=f.bind(c,this),e=e+(".delegateEvents"+this.cid);""===d?this.$el.bind(e,c):this.$el.delegate(d,e,c)}}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=0,c=w.length;b<c;b++){var d=w[b];a[d]&&(this[d]=a[d])}this.options=a},_ensureElement:function(){if(this.el)this.setElement(this.el,
!1);else{var a=n(this,"attributes")||{};this.id&&(a.id=this.id);this.className&&(a["class"]=this.className);this.setElement(this.make(this.tagName,a),!1)}}});o.extend=r.extend=u.extend=v.extend=function(a,b){var c=G(this,a,b);c.extend=this.extend;return c};var H={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};g.sync=function(a,b,c){var d=H[a];c||(c={});var e={type:d,dataType:"json"};c.url||(e.url=n(b,"url")||t());if(!c.data&&b&&("create"==a||"update"==a))e.contentType="application/json",
e.data=JSON.stringify(b.toJSON());g.emulateJSON&&(e.contentType="application/x-www-form-urlencoded",e.data=e.data?{model:e.data}:{});if(g.emulateHTTP&&("PUT"===d||"DELETE"===d))g.emulateJSON&&(e.data._method=d),e.type="POST",e.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",d)};"GET"!==e.type&&!g.emulateJSON&&(e.processData=!1);return i.ajax(f.extend(e,c))};g.wrapError=function(a,b,c){return function(d,e){e=d===b?e:d;a?a(b,e,c):b.trigger("error",b,e,c)}};var x=function(){},G=function(a,
b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){a.apply(this,arguments)};f.extend(d,a);x.prototype=a.prototype;d.prototype=new x;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},n=function(a,b){return!a||!a[b]?null:f.isFunction(a[b])?a[b]():a[b]},t=function(){throw Error('A "url" property or function must be specified');}}).call(this);

File diff suppressed because it is too large Load Diff

1571
vendor/backbone/1.0.0/backbone.js vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

431
vendor/flot/jquery.flot.time.js vendored Normal file
View File

@ -0,0 +1,431 @@
/* Pretty handling of time axes.
Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.
Set axis.mode to "time" to enable. See the section "Time series data" in
API.txt for details.
*/
(function($) {
var options = {
xaxis: {
timezone: null, // "browser" for local to the client or timezone for timezone-js
timeformat: null, // format string to use
twelveHourClock: false, // 12 or 24 time in time mode
monthNames: null // list of names of months
}
};
// round to nearby lower multiple of base
function floorInBase(n, base) {
return base * Math.floor(n / base);
}
// Returns a string with the date d formatted according to fmt.
// A subset of the Open Group's strftime format is supported.
function formatDate(d, fmt, monthNames, dayNames) {
if (typeof d.strftime == "function") {
return d.strftime(fmt);
}
var leftPad = function(n, pad) {
n = "" + n;
pad = "" + (pad == null ? "0" : pad);
return n.length == 1 ? pad + n : n;
};
var r = [];
var escape = false;
var hours = d.getHours();
var isAM = hours < 12;
if (monthNames == null) {
monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
}
if (dayNames == null) {
dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
}
var hours12;
if (hours > 12) {
hours12 = hours - 12;
} else if (hours == 0) {
hours12 = 12;
} else {
hours12 = hours;
}
for (var i = 0; i < fmt.length; ++i) {
var c = fmt.charAt(i);
if (escape) {
switch (c) {
case 'a': c = "" + dayNames[d.getDay()]; break;
case 'b': c = "" + monthNames[d.getMonth()]; break;
case 'd': c = leftPad(d.getDate()); break;
case 'e': c = leftPad(d.getDate(), " "); break;
case 'h': // For back-compat with 0.7; remove in 1.0
case 'H': c = leftPad(hours); break;
case 'I': c = leftPad(hours12); break;
case 'l': c = leftPad(hours12, " "); break;
case 'm': c = leftPad(d.getMonth() + 1); break;
case 'M': c = leftPad(d.getMinutes()); break;
// quarters not in Open Group's strftime specification
case 'q':
c = "" + (Math.floor(d.getMonth() / 3) + 1); break;
case 'S': c = leftPad(d.getSeconds()); break;
case 'y': c = leftPad(d.getFullYear() % 100); break;
case 'Y': c = "" + d.getFullYear(); break;
case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break;
case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break;
case 'w': c = "" + d.getDay(); break;
}
r.push(c);
escape = false;
} else {
if (c == "%") {
escape = true;
} else {
r.push(c);
}
}
}
return r.join("");
}
// To have a consistent view of time-based data independent of which time
// zone the client happens to be in we need a date-like object independent
// of time zones. This is done through a wrapper that only calls the UTC
// versions of the accessor methods.
function makeUtcWrapper(d) {
function addProxyMethod(sourceObj, sourceMethod, targetObj, targetMethod) {
sourceObj[sourceMethod] = function() {
return targetObj[targetMethod].apply(targetObj, arguments);
};
};
var utc = {
date: d
};
// support strftime, if found
if (d.strftime != undefined) {
addProxyMethod(utc, "strftime", d, "strftime");
}
addProxyMethod(utc, "getTime", d, "getTime");
addProxyMethod(utc, "setTime", d, "setTime");
var props = ["Date", "Day", "FullYear", "Hours", "Milliseconds", "Minutes", "Month", "Seconds"];
for (var p = 0; p < props.length; p++) {
addProxyMethod(utc, "get" + props[p], d, "getUTC" + props[p]);
addProxyMethod(utc, "set" + props[p], d, "setUTC" + props[p]);
}
return utc;
};
// select time zone strategy. This returns a date-like object tied to the
// desired timezone
function dateGenerator(ts, opts) {
if (opts.timezone == "browser") {
return new Date(ts);
} else if (!opts.timezone || opts.timezone == "utc") {
return makeUtcWrapper(new Date(ts));
} else if (typeof timezoneJS != "undefined" && typeof timezoneJS.Date != "undefined") {
var d = new timezoneJS.Date();
// timezone-js is fickle, so be sure to set the time zone before
// setting the time.
d.setTimezone(opts.timezone);
d.setTime(ts);
return d;
} else {
return makeUtcWrapper(new Date(ts));
}
}
// map of app. size of time units in milliseconds
var timeUnitSize = {
"second": 1000,
"minute": 60 * 1000,
"hour": 60 * 60 * 1000,
"day": 24 * 60 * 60 * 1000,
"month": 30 * 24 * 60 * 60 * 1000,
"quarter": 3 * 30 * 24 * 60 * 60 * 1000,
"year": 365.2425 * 24 * 60 * 60 * 1000
};
// the allowed tick sizes, after 1 year we use
// an integer algorithm
var baseSpec = [
[1, "second"], [2, "second"], [5, "second"], [10, "second"],
[30, "second"],
[1, "minute"], [2, "minute"], [5, "minute"], [10, "minute"],
[30, "minute"],
[1, "hour"], [2, "hour"], [4, "hour"],
[8, "hour"], [12, "hour"],
[1, "day"], [2, "day"], [3, "day"],
[0.25, "month"], [0.5, "month"], [1, "month"],
[2, "month"]
];
// we don't know which variant(s) we'll need yet, but generating both is
// cheap
var specMonths = baseSpec.concat([[3, "month"], [6, "month"],
[1, "year"]]);
var specQuarters = baseSpec.concat([[1, "quarter"], [2, "quarter"],
[1, "year"]]);
function init(plot) {
plot.hooks.processOptions.push(function (plot, options) {
$.each(plot.getAxes(), function(axisName, axis) {
var opts = axis.options;
if (opts.mode == "time") {
axis.tickGenerator = function(axis) {
var ticks = [];
var d = dateGenerator(axis.min, opts);
var minSize = 0;
// make quarter use a possibility if quarters are
// mentioned in either of these options
var spec = (opts.tickSize && opts.tickSize[1] ===
"quarter") ||
(opts.minTickSize && opts.minTickSize[1] ===
"quarter") ? specQuarters : specMonths;
if (opts.minTickSize != null) {
if (typeof opts.tickSize == "number") {
minSize = opts.tickSize;
} else {
minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];
}
}
for (var i = 0; i < spec.length - 1; ++i) {
if (axis.delta < (spec[i][0] * timeUnitSize[spec[i][1]]
+ spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2
&& spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) {
break;
}
}
var size = spec[i][0];
var unit = spec[i][1];
// special-case the possibility of several years
if (unit == "year") {
// if given a minTickSize in years, just use it,
// ensuring that it's an integer
if (opts.minTickSize != null && opts.minTickSize[1] == "year") {
size = Math.floor(opts.minTickSize[0]);
} else {
var magn = Math.pow(10, Math.floor(Math.log(axis.delta / timeUnitSize.year) / Math.LN10));
var norm = (axis.delta / timeUnitSize.year) / magn;
if (norm < 1.5) {
size = 1;
} else if (norm < 3) {
size = 2;
} else if (norm < 7.5) {
size = 5;
} else {
size = 10;
}
size *= magn;
}
// minimum size for years is 1
if (size < 1) {
size = 1;
}
}
axis.tickSize = opts.tickSize || [size, unit];
var tickSize = axis.tickSize[0];
unit = axis.tickSize[1];
var step = tickSize * timeUnitSize[unit];
if (unit == "second") {
d.setSeconds(floorInBase(d.getSeconds(), tickSize));
} else if (unit == "minute") {
d.setMinutes(floorInBase(d.getMinutes(), tickSize));
} else if (unit == "hour") {
d.setHours(floorInBase(d.getHours(), tickSize));
} else if (unit == "month") {
d.setMonth(floorInBase(d.getMonth(), tickSize));
} else if (unit == "quarter") {
d.setMonth(3 * floorInBase(d.getMonth() / 3,
tickSize));
} else if (unit == "year") {
d.setFullYear(floorInBase(d.getFullYear(), tickSize));
}
// reset smaller components
d.setMilliseconds(0);
if (step >= timeUnitSize.minute) {
d.setSeconds(0);
}
if (step >= timeUnitSize.hour) {
d.setMinutes(0);
}
if (step >= timeUnitSize.day) {
d.setHours(0);
}
if (step >= timeUnitSize.day * 4) {
d.setDate(1);
}
if (step >= timeUnitSize.month * 2) {
d.setMonth(floorInBase(d.getMonth(), 3));
}
if (step >= timeUnitSize.quarter * 2) {
d.setMonth(floorInBase(d.getMonth(), 6));
}
if (step >= timeUnitSize.year) {
d.setMonth(0);
}
var carry = 0;
var v = Number.NaN;
var prev;
do {
prev = v;
v = d.getTime();
ticks.push(v);
if (unit == "month" || unit == "quarter") {
if (tickSize < 1) {
// a bit complicated - we'll divide the
// month/quarter up but we need to take
// care of fractions so we don't end up in
// the middle of a day
d.setDate(1);
var start = d.getTime();
d.setMonth(d.getMonth() +
(unit == "quarter" ? 3 : 1));
var end = d.getTime();
d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);
carry = d.getHours();
d.setHours(0);
} else {
d.setMonth(d.getMonth() +
tickSize * (unit == "quarter" ? 3 : 1));
}
} else if (unit == "year") {
d.setFullYear(d.getFullYear() + tickSize);
} else {
d.setTime(v + step);
}
} while (v < axis.max && v != prev);
return ticks;
};
axis.tickFormatter = function (v, axis) {
var d = dateGenerator(v, axis.options);
// first check global format
if (opts.timeformat != null) {
return formatDate(d, opts.timeformat, opts.monthNames, opts.dayNames);
}
// possibly use quarters if quarters are mentioned in
// any of these places
var useQuarters = (axis.options.tickSize &&
axis.options.tickSize[1] == "quarter") ||
(axis.options.minTickSize &&
axis.options.minTickSize[1] == "quarter");
var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
var span = axis.max - axis.min;
var suffix = (opts.twelveHourClock) ? " %p" : "";
var hourCode = (opts.twelveHourClock) ? "%I" : "%H";
var fmt;
if (t < timeUnitSize.minute) {
fmt = hourCode + ":%M:%S" + suffix;
} else if (t < timeUnitSize.day) {
if (span < 2 * timeUnitSize.day) {
fmt = hourCode + ":%M" + suffix;
} else {
fmt = "%b %d " + hourCode + ":%M" + suffix;
}
} else if (t < timeUnitSize.month) {
fmt = "%b %d";
} else if ((useQuarters && t < timeUnitSize.quarter) ||
(!useQuarters && t < timeUnitSize.year)) {
if (span < timeUnitSize.year) {
fmt = "%b";
} else {
fmt = "%b %Y";
}
} else if (useQuarters && t < timeUnitSize.year) {
if (span < timeUnitSize.year) {
fmt = "Q%q";
} else {
fmt = "Q%q %Y";
}
} else {
fmt = "%Y";
}
var rt = formatDate(d, fmt, opts.monthNames, opts.dayNames);
return rt;
};
}
});
});
}
$.plot.plugins.push({
init: init,
options: options,
name: 'time',
version: '1.0'
});
// Time-axis support used to be in Flot core, which exposed the
// formatDate function on the plot object. Various plugins depend
// on the function, so we need to re-expose it here.
$.plot.formatDate = formatDate;
})(jQuery);

View File

@ -1,918 +0,0 @@
// moment.js
// version : 1.6.2
// author : Tim Wood
// license : MIT
// momentjs.com
(function (Date, undefined) {
var moment,
VERSION = "1.6.2",
round = Math.round, i,
// internal storage for language config files
languages = {},
currentLanguage = 'en',
// check for nodeJS
hasModule = (typeof module !== 'undefined'),
// parameters to check for on the lang config
langConfigProperties = 'months|monthsShort|monthsParse|weekdays|weekdaysShort|longDateFormat|calendar|relativeTime|ordinal|meridiem'.split('|'),
// ASP.NET json date format regex
aspNetJsonRegex = /^\/?Date\((\-?\d+)/i,
// format tokens
formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|dddd?|do?|w[o|w]?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|SS?S?|zz?|ZZ?|LT|LL?L?L?)/g,
// parsing tokens
parseMultipleFormatChunker = /([0-9a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+)/gi,
// parsing token regexes
parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99
parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999
parseTokenThreeDigits = /\d{3}/, // 000 - 999
parseTokenFourDigits = /\d{4}/, // 0000 - 9999
parseTokenWord = /[0-9a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+/i, // any word characters or numbers
parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/i, // +00:00 -00:00 +0000 -0000 or Z
parseTokenT = /T/i, // T (ISO seperator)
// preliminary iso regex
// 0000-00-00 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000
isoRegex = /^\s*\d{4}-\d\d-\d\d(T(\d\d(:\d\d(:\d\d(\.\d\d?\d?)?)?)?)?([\+\-]\d\d:?\d\d)?)?/,
isoFormat = 'YYYY-MM-DDTHH:mm:ssZ',
// iso time formats and regexes
isoTimes = [
['HH:mm:ss.S', /T\d\d:\d\d:\d\d\.\d{1,3}/],
['HH:mm:ss', /T\d\d:\d\d:\d\d/],
['HH:mm', /T\d\d:\d\d/],
['HH', /T\d\d/]
],
// timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"]
parseTimezoneChunker = /([\+\-]|\d\d)/gi,
// getter and setter names
proxyGettersAndSetters = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
unitMillisecondFactors = {
'Milliseconds' : 1,
'Seconds' : 1e3,
'Minutes' : 6e4,
'Hours' : 36e5,
'Days' : 864e5,
'Months' : 2592e6,
'Years' : 31536e6
};
// Moment prototype object
function Moment(date, isUTC) {
this._d = date;
this._isUTC = !!isUTC;
}
function absRound(number) {
if (number < 0) {
return Math.ceil(number);
} else {
return Math.floor(number);
}
}
// Duration Constructor
function Duration(duration) {
var data = this._data = {},
years = duration.years || duration.y || 0,
months = duration.months || duration.M || 0,
weeks = duration.weeks || duration.w || 0,
days = duration.days || duration.d || 0,
hours = duration.hours || duration.h || 0,
minutes = duration.minutes || duration.m || 0,
seconds = duration.seconds || duration.s || 0,
milliseconds = duration.milliseconds || duration.ms || 0;
// representation for dateAddRemove
this._milliseconds = milliseconds +
seconds * 1e3 + // 1000
minutes * 6e4 + // 1000 * 60
hours * 36e5; // 1000 * 60 * 60
// Because of dateAddRemove treats 24 hours as different from a
// day when working around DST, we need to store them separately
this._days = days +
weeks * 7;
// It is impossible translate months into days without knowing
// which months you are are talking about, so we have to store
// it separately.
this._months = months +
years * 12;
// The following code bubbles up values, see the tests for
// examples of what that means.
data.milliseconds = milliseconds % 1000;
seconds += absRound(milliseconds / 1000);
data.seconds = seconds % 60;
minutes += absRound(seconds / 60);
data.minutes = minutes % 60;
hours += absRound(minutes / 60);
data.hours = hours % 24;
days += absRound(hours / 24);
days += weeks * 7;
data.days = days % 30;
months += absRound(days / 30);
data.months = months % 12;
years += absRound(months / 12);
data.years = years;
}
// left zero fill a number
// see http://jsperf.com/left-zero-filling for performance comparison
function leftZeroFill(number, targetLength) {
var output = number + '';
while (output.length < targetLength) {
output = '0' + output;
}
return output;
}
// helper function for _.addTime and _.subtractTime
function addOrSubtractDurationFromMoment(mom, duration, isAdding) {
var ms = duration._milliseconds,
d = duration._days,
M = duration._months,
currentDate;
if (ms) {
mom._d.setTime(+mom + ms * isAdding);
}
if (d) {
mom.date(mom.date() + d * isAdding);
}
if (M) {
currentDate = mom.date();
mom.date(1)
.month(mom.month() + M * isAdding)
.date(Math.min(currentDate, mom.daysInMonth()));
}
}
// check if is an array
function isArray(input) {
return Object.prototype.toString.call(input) === '[object Array]';
}
// convert an array to a date.
// the array should mirror the parameters below
// note: all values past the year are optional and will default to the lowest possible value.
// [year, month, day , hour, minute, second, millisecond]
function dateFromArray(input) {
return new Date(input[0], input[1] || 0, input[2] || 1, input[3] || 0, input[4] || 0, input[5] || 0, input[6] || 0);
}
// format date using native date object
function formatMoment(m, inputString) {
var currentMonth = m.month(),
currentDate = m.date(),
currentYear = m.year(),
currentDay = m.day(),
currentHours = m.hours(),
currentMinutes = m.minutes(),
currentSeconds = m.seconds(),
currentMilliseconds = m.milliseconds(),
currentZone = -m.zone(),
ordinal = moment.ordinal,
meridiem = moment.meridiem;
// check if the character is a format
// return formatted string or non string.
//
// uses switch/case instead of an object of named functions (like http://phpjs.org/functions/date:380)
// for minification and performance
// see http://jsperf.com/object-of-functions-vs-switch for performance comparison
function replaceFunction(input) {
// create a couple variables to be used later inside one of the cases.
var a, b;
switch (input) {
// MONTH
case 'M' :
return currentMonth + 1;
case 'Mo' :
return (currentMonth + 1) + ordinal(currentMonth + 1);
case 'MM' :
return leftZeroFill(currentMonth + 1, 2);
case 'MMM' :
return moment.monthsShort[currentMonth];
case 'MMMM' :
return moment.months[currentMonth];
// DAY OF MONTH
case 'D' :
return currentDate;
case 'Do' :
return currentDate + ordinal(currentDate);
case 'DD' :
return leftZeroFill(currentDate, 2);
// DAY OF YEAR
case 'DDD' :
a = new Date(currentYear, currentMonth, currentDate);
b = new Date(currentYear, 0, 1);
return ~~ (((a - b) / 864e5) + 1.5);
case 'DDDo' :
a = replaceFunction('DDD');
return a + ordinal(a);
case 'DDDD' :
return leftZeroFill(replaceFunction('DDD'), 3);
// WEEKDAY
case 'd' :
return currentDay;
case 'do' :
return currentDay + ordinal(currentDay);
case 'ddd' :
return moment.weekdaysShort[currentDay];
case 'dddd' :
return moment.weekdays[currentDay];
// WEEK OF YEAR
case 'w' :
a = new Date(currentYear, currentMonth, currentDate - currentDay + 5);
b = new Date(a.getFullYear(), 0, 4);
return ~~ ((a - b) / 864e5 / 7 + 1.5);
case 'wo' :
a = replaceFunction('w');
return a + ordinal(a);
case 'ww' :
return leftZeroFill(replaceFunction('w'), 2);
// YEAR
case 'YY' :
return leftZeroFill(currentYear % 100, 2);
case 'YYYY' :
return currentYear;
// AM / PM
case 'a' :
return meridiem ? meridiem(currentHours, currentMinutes, false) : (currentHours > 11 ? 'pm' : 'am');
case 'A' :
return meridiem ? meridiem(currentHours, currentMinutes, true) : (currentHours > 11 ? 'PM' : 'AM');
// 24 HOUR
case 'H' :
return currentHours;
case 'HH' :
return leftZeroFill(currentHours, 2);
// 12 HOUR
case 'h' :
return currentHours % 12 || 12;
case 'hh' :
return leftZeroFill(currentHours % 12 || 12, 2);
// MINUTE
case 'm' :
return currentMinutes;
case 'mm' :
return leftZeroFill(currentMinutes, 2);
// SECOND
case 's' :
return currentSeconds;
case 'ss' :
return leftZeroFill(currentSeconds, 2);
// MILLISECONDS
case 'S' :
return ~~ (currentMilliseconds / 100);
case 'SS' :
return leftZeroFill(~~(currentMilliseconds / 10), 2);
case 'SSS' :
return leftZeroFill(currentMilliseconds, 3);
// TIMEZONE
case 'Z' :
return (currentZone < 0 ? '-' : '+') + leftZeroFill(~~(Math.abs(currentZone) / 60), 2) + ':' + leftZeroFill(~~(Math.abs(currentZone) % 60), 2);
case 'ZZ' :
return (currentZone < 0 ? '-' : '+') + leftZeroFill(~~(10 * Math.abs(currentZone) / 6), 4);
// LONG DATES
case 'L' :
case 'LL' :
case 'LLL' :
case 'LLLL' :
case 'LT' :
return formatMoment(m, moment.longDateFormat[input]);
// DEFAULT
default :
return input.replace(/(^\[)|(\\)|\]$/g, "");
}
}
return inputString.replace(formattingTokens, replaceFunction);
}
// get the regex to find the next token
function getParseRegexForToken(token) {
switch (token) {
case 'DDDD':
return parseTokenThreeDigits;
case 'YYYY':
return parseTokenFourDigits;
case 'S':
case 'SS':
case 'SSS':
case 'DDD':
return parseTokenOneToThreeDigits;
case 'MMM':
case 'MMMM':
case 'ddd':
case 'dddd':
case 'a':
case 'A':
return parseTokenWord;
case 'Z':
case 'ZZ':
return parseTokenTimezone;
case 'T':
return parseTokenT;
case 'MM':
case 'DD':
case 'dd':
case 'YY':
case 'HH':
case 'hh':
case 'mm':
case 'ss':
case 'M':
case 'D':
case 'd':
case 'H':
case 'h':
case 'm':
case 's':
return parseTokenOneOrTwoDigits;
default :
return new RegExp(token.replace('\\', ''));
}
}
// function to convert string input to date
function addTimeToArrayFromToken(token, input, datePartArray, config) {
var a;
//console.log('addTime', format, input);
switch (token) {
// MONTH
case 'M' : // fall through to MM
case 'MM' :
datePartArray[1] = (input == null) ? 0 : ~~input - 1;
break;
case 'MMM' : // fall through to MMMM
case 'MMMM' :
for (a = 0; a < 12; a++) {
if (moment.monthsParse[a].test(input)) {
datePartArray[1] = a;
break;
}
}
break;
// DAY OF MONTH
case 'D' : // fall through to DDDD
case 'DD' : // fall through to DDDD
case 'DDD' : // fall through to DDDD
case 'DDDD' :
datePartArray[2] = ~~input;
break;
// YEAR
case 'YY' :
input = ~~input;
datePartArray[0] = input + (input > 70 ? 1900 : 2000);
break;
case 'YYYY' :
datePartArray[0] = ~~Math.abs(input);
break;
// AM / PM
case 'a' : // fall through to A
case 'A' :
config.isPm = ((input + '').toLowerCase() === 'pm');
break;
// 24 HOUR
case 'H' : // fall through to hh
case 'HH' : // fall through to hh
case 'h' : // fall through to hh
case 'hh' :
datePartArray[3] = ~~input;
break;
// MINUTE
case 'm' : // fall through to mm
case 'mm' :
datePartArray[4] = ~~input;
break;
// SECOND
case 's' : // fall through to ss
case 'ss' :
datePartArray[5] = ~~input;
break;
// MILLISECOND
case 'S' :
case 'SS' :
case 'SSS' :
datePartArray[6] = ~~ (('0.' + input) * 1000);
break;
// TIMEZONE
case 'Z' : // fall through to ZZ
case 'ZZ' :
config.isUTC = true;
a = (input + '').match(parseTimezoneChunker);
if (a && a[1]) {
config.tzh = ~~a[1];
}
if (a && a[2]) {
config.tzm = ~~a[2];
}
// reverse offsets
if (a && a[0] === '+') {
config.tzh = -config.tzh;
config.tzm = -config.tzm;
}
break;
}
}
// date from string and format string
function makeDateFromStringAndFormat(string, format) {
var datePartArray = [0, 0, 1, 0, 0, 0, 0],
config = {
tzh : 0, // timezone hour offset
tzm : 0 // timezone minute offset
},
tokens = format.match(formattingTokens),
i, parsedInput;
for (i = 0; i < tokens.length; i++) {
parsedInput = (getParseRegexForToken(tokens[i]).exec(string) || [])[0];
string = string.replace(getParseRegexForToken(tokens[i]), '');
addTimeToArrayFromToken(tokens[i], parsedInput, datePartArray, config);
}
// handle am pm
if (config.isPm && datePartArray[3] < 12) {
datePartArray[3] += 12;
}
// if is 12 am, change hours to 0
if (config.isPm === false && datePartArray[3] === 12) {
datePartArray[3] = 0;
}
// handle timezone
datePartArray[3] += config.tzh;
datePartArray[4] += config.tzm;
// return
return config.isUTC ? new Date(Date.UTC.apply({}, datePartArray)) : dateFromArray(datePartArray);
}
// compare two arrays, return the number of differences
function compareArrays(array1, array2) {
var len = Math.min(array1.length, array2.length),
lengthDiff = Math.abs(array1.length - array2.length),
diffs = 0,
i;
for (i = 0; i < len; i++) {
if (~~array1[i] !== ~~array2[i]) {
diffs++;
}
}
return diffs + lengthDiff;
}
// date from string and array of format strings
function makeDateFromStringAndArray(string, formats) {
var output,
inputParts = string.match(parseMultipleFormatChunker) || [],
formattedInputParts,
scoreToBeat = 99,
i,
currentDate,
currentScore;
for (i = 0; i < formats.length; i++) {
currentDate = makeDateFromStringAndFormat(string, formats[i]);
formattedInputParts = formatMoment(new Moment(currentDate), formats[i]).match(parseMultipleFormatChunker) || [];
currentScore = compareArrays(inputParts, formattedInputParts);
if (currentScore < scoreToBeat) {
scoreToBeat = currentScore;
output = currentDate;
}
}
return output;
}
// date from iso format
function makeDateFromString(string) {
var format = 'YYYY-MM-DDT',
i;
if (isoRegex.exec(string)) {
for (i = 0; i < 4; i++) {
if (isoTimes[i][1].exec(string)) {
format += isoTimes[i][0];
break;
}
}
return parseTokenTimezone.exec(string) ?
makeDateFromStringAndFormat(string, format + ' Z') :
makeDateFromStringAndFormat(string, format);
}
return new Date(string);
}
// helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize
function substituteTimeAgo(string, number, withoutSuffix, isFuture) {
var rt = moment.relativeTime[string];
return (typeof rt === 'function') ?
rt(number || 1, !!withoutSuffix, string, isFuture) :
rt.replace(/%d/i, number || 1);
}
function relativeTime(milliseconds, withoutSuffix) {
var seconds = round(Math.abs(milliseconds) / 1000),
minutes = round(seconds / 60),
hours = round(minutes / 60),
days = round(hours / 24),
years = round(days / 365),
args = seconds < 45 && ['s', seconds] ||
minutes === 1 && ['m'] ||
minutes < 45 && ['mm', minutes] ||
hours === 1 && ['h'] ||
hours < 22 && ['hh', hours] ||
days === 1 && ['d'] ||
days <= 25 && ['dd', days] ||
days <= 45 && ['M'] ||
days < 345 && ['MM', round(days / 30)] ||
years === 1 && ['y'] || ['yy', years];
args[2] = withoutSuffix;
args[3] = milliseconds > 0;
return substituteTimeAgo.apply({}, args);
}
moment = function (input, format) {
if (input === null || input === '') {
return null;
}
var date,
matched,
isUTC;
// parse Moment object
if (moment.isMoment(input)) {
date = new Date(+input._d);
isUTC = input._isUTC;
// parse string and format
} else if (format) {
if (isArray(format)) {
date = makeDateFromStringAndArray(input, format);
} else {
date = makeDateFromStringAndFormat(input, format);
}
// evaluate it as a JSON-encoded date
} else {
matched = aspNetJsonRegex.exec(input);
date = input === undefined ? new Date() :
matched ? new Date(+matched[1]) :
input instanceof Date ? input :
isArray(input) ? dateFromArray(input) :
typeof input === 'string' ? makeDateFromString(input) :
new Date(input);
}
return new Moment(date, isUTC);
};
// creating with utc
moment.utc = function (input, format) {
if (isArray(input)) {
return new Moment(new Date(Date.UTC.apply({}, input)), true);
}
return (format && input) ?
moment(input + ' +0000', format + ' Z').utc() :
moment(input && !parseTokenTimezone.exec(input) ? input + '+0000' : input).utc();
};
// creating with unix timestamp (in seconds)
moment.unix = function (input) {
return moment(input * 1000);
};
// duration
moment.duration = function (input, key) {
var isDuration = moment.isDuration(input),
isNumber = (typeof input === 'number'),
duration = (isDuration ? input._data : (isNumber ? {} : input));
if (isNumber) {
if (key) {
duration[key] = input;
} else {
duration.milliseconds = input;
}
}
return new Duration(duration);
};
// humanizeDuration
// This method is deprecated in favor of the new Duration object. Please
// see the moment.duration method.
moment.humanizeDuration = function (num, type, withSuffix) {
return moment.duration(num, type === true ? null : type).humanize(type === true ? true : withSuffix);
};
// version number
moment.version = VERSION;
// default format
moment.defaultFormat = isoFormat;
// language switching and caching
moment.lang = function (key, values) {
var i, req,
parse = [];
if (!key) {
return currentLanguage;
}
if (values) {
for (i = 0; i < 12; i++) {
parse[i] = new RegExp('^' + values.months[i] + '|^' + values.monthsShort[i].replace('.', ''), 'i');
}
values.monthsParse = values.monthsParse || parse;
languages[key] = values;
}
if (languages[key]) {
for (i = 0; i < langConfigProperties.length; i++) {
moment[langConfigProperties[i]] = languages[key][langConfigProperties[i]] ||
languages.en[langConfigProperties[i]];
}
currentLanguage = key;
} else {
if (hasModule) {
req = require('./lang/' + key);
moment.lang(key, req);
}
}
};
// set default language
moment.lang('en', {
months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"),
monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),
weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),
weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),
longDateFormat : {
LT : "h:mm A",
L : "MM/DD/YYYY",
LL : "MMMM D YYYY",
LLL : "MMMM D YYYY LT",
LLLL : "dddd, MMMM D YYYY LT"
},
meridiem : false,
calendar : {
sameDay : '[Today at] LT',
nextDay : '[Tomorrow at] LT',
nextWeek : 'dddd [at] LT',
lastDay : '[Yesterday at] LT',
lastWeek : '[last] dddd [at] LT',
sameElse : 'L'
},
relativeTime : {
future : "in %s",
past : "%s ago",
s : "a few seconds",
m : "a minute",
mm : "%d minutes",
h : "an hour",
hh : "%d hours",
d : "a day",
dd : "%d days",
M : "a month",
MM : "%d months",
y : "a year",
yy : "%d years"
},
ordinal : function (number) {
var b = number % 10;
return (~~ (number % 100 / 10) === 1) ? 'th' :
(b === 1) ? 'st' :
(b === 2) ? 'nd' :
(b === 3) ? 'rd' : 'th';
}
});
// compare moment object
moment.isMoment = function (obj) {
return obj instanceof Moment;
};
// for typechecking Duration objects
moment.isDuration = function (obj) {
return obj instanceof Duration;
};
// shortcut for prototype
moment.fn = Moment.prototype = {
clone : function () {
return moment(this);
},
valueOf : function () {
return +this._d;
},
unix : function () {
return Math.floor(+this._d / 1000);
},
toString : function () {
return this._d.toString();
},
toDate : function () {
return this._d;
},
utc : function () {
this._isUTC = true;
return this;
},
local : function () {
this._isUTC = false;
return this;
},
format : function (inputString) {
return formatMoment(this, inputString ? inputString : moment.defaultFormat);
},
add : function (input, val) {
var dur = val ? moment.duration(+val, input) : moment.duration(input);
addOrSubtractDurationFromMoment(this, dur, 1);
return this;
},
subtract : function (input, val) {
var dur = val ? moment.duration(+val, input) : moment.duration(input);
addOrSubtractDurationFromMoment(this, dur, -1);
return this;
},
diff : function (input, val, asFloat) {
var inputMoment = this._isUTC ? moment(input).utc() : moment(input).local(),
zoneDiff = (this.zone() - inputMoment.zone()) * 6e4,
diff = this._d - inputMoment._d - zoneDiff,
year = this.year() - inputMoment.year(),
month = this.month() - inputMoment.month(),
date = this.date() - inputMoment.date(),
output;
if (val === 'months') {
output = year * 12 + month + date / 30;
} else if (val === 'years') {
output = year + (month + date / 30) / 12;
} else {
output = val === 'seconds' ? diff / 1e3 : // 1000
val === 'minutes' ? diff / 6e4 : // 1000 * 60
val === 'hours' ? diff / 36e5 : // 1000 * 60 * 60
val === 'days' ? diff / 864e5 : // 1000 * 60 * 60 * 24
val === 'weeks' ? diff / 6048e5 : // 1000 * 60 * 60 * 24 * 7
diff;
}
return asFloat ? output : round(output);
},
from : function (time, withoutSuffix) {
return moment.duration(this.diff(time)).humanize(!withoutSuffix);
},
fromNow : function (withoutSuffix) {
return this.from(moment(), withoutSuffix);
},
calendar : function () {
var diff = this.diff(moment().sod(), 'days', true),
calendar = moment.calendar,
allElse = calendar.sameElse,
format = diff < -6 ? allElse :
diff < -1 ? calendar.lastWeek :
diff < 0 ? calendar.lastDay :
diff < 1 ? calendar.sameDay :
diff < 2 ? calendar.nextDay :
diff < 7 ? calendar.nextWeek : allElse;
return this.format(typeof format === 'function' ? format.apply(this) : format);
},
isLeapYear : function () {
var year = this.year();
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
},
isDST : function () {
return (this.zone() < moment([this.year()]).zone() ||
this.zone() < moment([this.year(), 5]).zone());
},
day : function (input) {
var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay();
return input == null ? day :
this.add({ d : input - day });
},
sod: function () {
return moment(this)
.hours(0)
.minutes(0)
.seconds(0)
.milliseconds(0);
},
eod: function () {
// end of day = start of day plus 1 day, minus 1 millisecond
return this.sod().add({
d : 1,
ms : -1
});
},
zone : function () {
return this._isUTC ? 0 : this._d.getTimezoneOffset();
},
daysInMonth : function () {
return moment(this).month(this.month() + 1).date(0).date();
}
};
// helper for adding shortcuts
function makeGetterAndSetter(name, key) {
moment.fn[name] = function (input) {
var utc = this._isUTC ? 'UTC' : '';
if (input != null) {
this._d['set' + utc + key](input);
return this;
} else {
return this._d['get' + utc + key]();
}
};
}
// loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds)
for (i = 0; i < proxyGettersAndSetters.length; i ++) {
makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase(), proxyGettersAndSetters[i]);
}
// add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear')
makeGetterAndSetter('year', 'FullYear');
moment.duration.fn = Duration.prototype = {
weeks : function () {
return absRound(this.days() / 7);
},
valueOf : function () {
return this._milliseconds +
this._days * 864e5 +
this._months * 2592e6;
},
humanize : function (withSuffix) {
var difference = +this,
rel = moment.relativeTime,
output = relativeTime(difference, !withSuffix);
if (withSuffix) {
output = (difference <= 0 ? rel.past : rel.future).replace(/%s/i, output);
}
return output;
}
};
function makeDurationGetter(name) {
moment.duration.fn[name] = function () {
return this._data[name];
};
}
function makeDurationAsGetter(name, factor) {
moment.duration.fn['as' + name] = function () {
return +this / factor;
};
}
for (i in unitMillisecondFactors) {
if (unitMillisecondFactors.hasOwnProperty(i)) {
makeDurationAsGetter(i, unitMillisecondFactors[i]);
makeDurationGetter(i.toLowerCase());
}
}
makeDurationAsGetter('Weeks', 6048e5);
// CommonJS module is defined
if (hasModule) {
module.exports = moment;
}
/*global ender:false */
if (typeof window !== 'undefined' && typeof ender === 'undefined') {
window.moment = moment;
}
/*global define:false */
if (typeof define === "function" && define.amd) {
define("moment", [], function () {
return moment;
});
}
})(Date);

1400
vendor/moment/2.0.0/moment.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,180 +0,0 @@
.vmm-timeline{}.vmm-timeline div *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
.vmm-timeline h1,.vmm-timeline h2,.vmm-timeline h3,.vmm-timeline h4,.vmm-timeline h5,.vmm-timeline h6,.vmm-timeline p,.vmm-timeline blockquote,.vmm-timeline pre,.vmm-timeline a,.vmm-timeline abbr,.vmm-timeline acronym,.vmm-timeline address,.vmm-timeline cite,.vmm-timeline code,.vmm-timeline del,.vmm-timeline dfn,.vmm-timeline em,.vmm-timeline img,.vmm-timeline q,.vmm-timeline s,.vmm-timeline samp,.vmm-timeline small,.vmm-timeline strike,.vmm-timeline strong,.vmm-timeline sub,.vmm-timeline sup,.vmm-timeline tt,.vmm-timeline var,.vmm-timeline dd,.vmm-timeline dl,.vmm-timeline dt,.vmm-timeline li,.vmm-timeline ol,.vmm-timeline ul,.vmm-timeline fieldset,.vmm-timeline form,.vmm-timeline label,.vmm-timeline legend,.vmm-timeline button,.vmm-timeline table,.vmm-timeline caption,.vmm-timeline tbody,.vmm-timeline tfoot,.vmm-timeline thead,.vmm-timeline tr,.vmm-timeline th,.vmm-timeline td,.vmm-timeline .container,.vmm-timeline .content-container,.vmm-timeline .media,.vmm-timeline .text,.vmm-timeline .slider,.vmm-timeline .date,.vmm-timeline .title,.vmm-timeline .messege,.vmm-timeline .map,.vmm-timeline .credit,.vmm-timeline .caption,.vmm-timeline .feedback,.vmm-timeline .feature,.vmm-timeline .toolbar,.vmm-timeline .marker,.vmm-timeline .dot,.vmm-timeline .line,.vmm-timeline .flag,.vmm-timeline .time,.vmm-timeline .era,.vmm-timeline .major,.vmm-timeline .minor,.vmm-timeline .navigation,.vmm-timeline .start,.vmm-timeline .active{margin:0;padding:0;border:0;font-weight:normal;font-style:normal;font-size:100%;line-height:1;font-family:inherit;}
.vmm-timeline table{border-collapse:collapse;border-spacing:0;}
.vmm-timeline ol,.vmm-timeline ul{list-style:none;}
.vmm-timeline q:before,.vmm-timeline q:after,.vmm-timeline blockquote:before,.vmm-timeline blockquote:after{content:"";}
.vmm-timeline a:focus{outline:thin dotted;}
.vmm-timeline a:hover,.vmm-timeline a:active{outline:0;}
.vmm-timeline article,.vmm-timeline aside,.vmm-timeline details,.vmm-timeline figcaption,.vmm-timeline figure,.vmm-timeline footer,.vmm-timeline header,.vmm-timeline hgroup,.vmm-timeline nav,.vmm-timeline section{display:block;}
.vmm-timeline audio,.vmm-timeline canvas,.vmm-timeline video{display:inline-block;*display:inline;*zoom:1;}
.vmm-timeline audio:not([controls]){display:none;}
.vmm-timeline div{max-width:none;}
.vmm-timeline sub,.vmm-timeline sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}
.vmm-timeline sup{top:-0.5em;}
.vmm-timeline sub{bottom:-0.25em;}
.vmm-timeline img{border:0;-ms-interpolation-mode:bicubic;}
.vmm-timeline button,.vmm-timeline input,.vmm-timeline select,.vmm-timeline textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;}
.vmm-timeline button,.vmm-timeline input{line-height:normal;*overflow:visible;}
.vmm-timeline button::-moz-focus-inner,.vmm-timeline input::-moz-focus-inner{border:0;padding:0;}
.vmm-timeline button,.vmm-timeline input[type="button"],.vmm-timeline input[type="reset"],.vmm-timeline input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
.vmm-timeline input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
.vmm-timeline input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}
.vmm-timeline textarea{overflow:auto;vertical-align:top;}
.vmm-timeline{font-family:"Georgia",Times New Roman,Times,serif;}.vmm-timeline .twitter,.vmm-timeline .messege,.vmm-timeline .credit,.vmm-timeline .caption,.vmm-timeline .zoom-in,.vmm-timeline .zoom-out,.vmm-timeline .back-home,.vmm-timeline .time-interval div,.vmm-timeline .time-interval-major div,.vmm-timeline .nav-container{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif !important;}
.vmm-timeline h1.date,.vmm-timeline h2.date,.vmm-timeline h3.date,.vmm-timeline h4.date,.vmm-timeline h5.date,.vmm-timeline h6.date{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif !important;}
.vmm-timeline .timenav h1,.vmm-timeline .flag-content h1,.vmm-timeline .era h1,.vmm-timeline .timenav h2,.vmm-timeline .flag-content h2,.vmm-timeline .era h2,.vmm-timeline .timenav h3,.vmm-timeline .flag-content h3,.vmm-timeline .era h3,.vmm-timeline .timenav h4,.vmm-timeline .flag-content h4,.vmm-timeline .era h4,.vmm-timeline .timenav h5,.vmm-timeline .flag-content h5,.vmm-timeline .era h5,.vmm-timeline .timenav h6,.vmm-timeline .flag-content h6,.vmm-timeline .era h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif !important;}
.vmm-timeline p,.vmm-timeline blockquote,.vmm-timeline blockquote p,.vmm-timeline .twitter blockquote p{font-family:"Georgia",Times New Roman,Times,serif !important;}
.vmm-timeline .feature h1,.vmm-timeline .feature h2,.vmm-timeline .feature h3,.vmm-timeline .feature h4,.vmm-timeline .feature h5,.vmm-timeline .feature h6{font-family:"Georgia",Times New Roman,Times,serif;}
.tooltip{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
#timeline-embed{background-color:#ffffff;margin-bottom:20px;border:1px solid #cccccc;padding-top:20px;padding-bottom:20px;clear:both;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;-webkit-box-shadow:1px 1px 3px rgba(0, 0, 0, 0.35);-moz-box-shadow:1px 1px 3px rgba(0, 0, 0, 0.35);box-shadow:1px 1px 3px rgba(0, 0, 0, 0.35);}
#timeline-embed.full-embed{overflow:hidden;border:0 !important;padding:0 !important;margin:0 !important;clear:both;-webkit-border-radius:0 !important;-moz-border-radius:0 !important;border-radius:0 !important;-webkit-box-shadow:0 0px 0px rgba(0, 0, 0, 0.25) !important;-moz-box-shadow:0 0px 0px rgba(0, 0, 0, 0.25) !important;box-shadow:0px 0px 0px rgba(0, 0, 0, 0.25) !important;}
#timeline-embed.sized-embed{overflow:hidden;border:1px solid #cccccc;padding-top:7px;padding-bottom:7px;margin:0 !important;clear:both;-webkit-box-shadow:0 0px 0px rgba(0, 0, 0, 0.25) !important;-moz-box-shadow:0 0px 0px rgba(0, 0, 0, 0.25) !important;box-shadow:0px 0px 0px rgba(0, 0, 0, 0.25) !important;}
.vmm-timeline{width:100%;height:100%;padding:0px;margin:0px;background-color:#ffffff;position:absolute;z-index:100;clear:both;overflow:hidden;}.vmm-timeline .feature{width:100%;}.vmm-timeline .feature .slider{width:100%;float:left;position:relative;z-index:10;padding-top:15px;-webkit-box-shadow:1px 1px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:1px 1px 7px rgba(0, 0, 0, 0.3);box-shadow:1px 1px 7px rgba(0, 0, 0, 0.3);}
.vmm-timeline .feedback{position:absolute;display:table;overflow:hidden;top:0px;left:0px;z-index:205;width:100%;height:100%;background-color:#ffffff;}.vmm-timeline .feedback .messege{display:table-cell;vertical-align:middle;font-size:15px;font-weight:normal;line-height:20px;width:75%;margin-left:auto;margin-right:auto;margin-top:auto;margin-bottom:auto;text-align:center;}
.vmm-timeline .container.main{position:absolute;top:0px: left:0px;padding-bottom:3px;width:auto;height:auto;margin:0px;clear:both;}
.vmm-timeline img,.vmm-timeline embed,.vmm-timeline object,.vmm-timeline video,.vmm-timeline iframe{max-width:100%;}
.vmm-timeline img{max-height:100%;border:1px solid #999999;}
.vmm-timeline a{color:#0088cc;text-decoration:none;}
.vmm-timeline a:hover{color:#005580;text-decoration:underline;}
.vmm-timeline .thumbnail{width:24px;height:24px;overflow:hidden;float:left;margin-right:1px;margin-top:6px;}
.vmm-timeline .thumbnail.thumb-plaintext{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-280px -48px;}
.vmm-timeline .thumbnail.thumb-quote{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-232px -48px;}
.vmm-timeline .thumbnail.thumb-document{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-256px -48px;}
.vmm-timeline .thumbnail.thumb-photo{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-280px -24px;border:0;}.vmm-timeline .thumbnail.thumb-photo img{border:0px none #cccccc !important;}
.vmm-timeline .thumbnail.thumb-twitter{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-256px -24px;}
.vmm-timeline .thumbnail.thumb-vimeo{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-328px -48px;}
.vmm-timeline .thumbnail.thumb-youtube{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-328px -72px;}
.vmm-timeline .thumbnail.thumb-video{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-328px -24px;}
.vmm-timeline .thumbnail.thumb-audio{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-304px -24px;}
.vmm-timeline .thumbnail.thumb-map{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-208px -48px;}
.vmm-timeline .thumbnail.thumb-website{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-232px -24px;}
.vmm-timeline .thumbnail.thumb-wikipedia{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-184px -48px;}
.vmm-timeline .zFront{z-index:204;}
.slider{width:100%;height:100%;overflow:hidden;}.slider .slider-container-mask{text-align:center;width:100%;height:100%;overflow:hidden;}.slider .slider-container-mask .slider-container{position:absolute;top:0px;left:-2160px;width:100%;height:100%;text-align:center;display:block;background-color:#ffffff;}.slider .slider-container-mask .slider-container .slider-item-container{display:table-cell;vertical-align:middle;}
.slider img,.slider embed,.slider object,.slider video,.slider iframe{max-width:100%;}
.slider .nav-previous,.slider .nav-next{position:absolute;top:0px;width:100px;color:#DBDBDB;font-size:11px;}.slider .nav-previous .nav-container,.slider .nav-next .nav-container{height:100px;width:100px;position:absolute;}
.slider .nav-previous .icon,.slider .nav-next .icon{margin-top:12px;margin-bottom:15px;}
.slider .nav-previous .date,.slider .nav-next .date,.slider .nav-previous .title,.slider .nav-next .title{line-height:14px;}.slider .nav-previous .date a,.slider .nav-next .date a,.slider .nav-previous .title a,.slider .nav-next .title a{color:#999999;}
.slider .nav-previous .date small,.slider .nav-next .date small,.slider .nav-previous .title small,.slider .nav-next .title small{display:none;}
.slider .nav-previous .date,.slider .nav-next .date{font-size:13px;line-height:13px;font-weight:bold;text-transform:uppercase;margin-bottom:5px;}
.slider .nav-previous .title,.slider .nav-next .title{font-size:11px;line-height:13px;}
.slider .nav-previous:hover,.slider .nav-next:hover{color:#333333;cursor:pointer;}
.slider .nav-previous{float:left;text-align:left;}.slider .nav-previous .icon{margin-left:15px;height:24px;background-image:url(timeline.png);background-repeat:no-repeat;background-position:-160px 0;width:24px;height:24px;overflow:hidden;}
.slider .nav-previous .date,.slider .nav-previous .title{text-align:left;padding-left:15px;}
.slider .nav-previous:hover .icon{margin-left:10px;}
.slider .nav-next{float:right;text-align:right;}.slider .nav-next .icon{margin-left:61px;background-image:url(timeline.png);background-repeat:no-repeat;background-position:-184px 0;width:24px;height:24px;height:24px;overflow:hidden;}
.slider .nav-next .date,.slider .nav-next .title{text-align:right;padding-right:15px;}
.slider .nav-next:hover .icon{margin-left:66px;}
.slider .slider-item{position:absolute;width:700px;height:100%;padding:0px;margin:0px;display:table;overflow-y:auto;}.slider .slider-item .content{display:table-cell;vertical-align:middle;}.slider .slider-item .content .pad-top .text .container{padding-top:15px;}
.slider .slider-item .content .pad-right .text .container{padding-right:15px;}
.slider .slider-item .content .pad-left .text .container{padding-left:30px;}
.slider .slider-item .content .pad-left .media.text-media .media-wrapper .media-container{border:none;background-color:#ffffff;}
.slider .slider-item .content .content-container{display:table;vertical-align:middle;}.slider .slider-item .content .content-container .text{width:40%;max-width:50%;min-width:120px;display:table-cell;vertical-align:middle;}.slider .slider-item .content .content-container .text .container{display:table-cell;vertical-align:middle;text-align:left;}.slider .slider-item .content .content-container .text .container p{-webkit-hyphens:auto;-moz-hyphens:auto;-ms-hyphens:auto;hyphens:auto;word-wrap:break-word;}
.slider .slider-item .content .content-container .media{width:100%;min-width:50%;float:left;}.slider .slider-item .content .content-container .media .media-wrapper{margin-left:auto;margin-right:auto;}.slider .slider-item .content .content-container .media .media-wrapper .media-container{display:inline-block;line-height:0px;padding:0px;max-height:100%;}.slider .slider-item .content .content-container .media .media-wrapper .media-container .media-frame,.slider .slider-item .content .content-container .media .media-wrapper .media-container .media-image img{border:1px solid;border-color:#cccccc #999999 #999999 #cccccc;background:#ffffff;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .soundcloud{border:0;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .media-image{display:inline-block;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .media-shadow{position:relative;z-index:1;background:#ffffff;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .media-shadow:before,.slider .slider-item .content .content-container .media .media-wrapper .media-container .media-shadow:after{z-index:-1;position:absolute;content:"";bottom:15px;left:10px;width:50%;top:80%;max-width:300px;background:#999999;-webkit-box-shadow:0 15px 10px #999999;-moz-box-shadow:0 15px 10px #999999;box-shadow:0 15px 10px #999999;-webkit-transform:rotate(-2deg);-moz-transform:rotate(-2deg);-o-transform:rotate(-2deg);-ms-transform:rotate(-2deg);transform:rotate(-2deg);}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .media-shadow::after{-webkit-transform:rotate(2deg);-moz-transform:rotate(2deg);-o-transform:rotate(2deg);-ms-transform:rotate(2deg);transform:rotate(2deg);right:10px;left:auto;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container span.messege{display:block;vertical-align:middle;margin-left:auto;margin-right:auto;text-align:center;background:#ffffff;font-size:28px;font-weight:bold;text-transform:uppercase;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .plain-text{display:table;}.slider .slider-item .content .content-container .media .media-wrapper .media-container .plain-text .container{display:table-cell;vertical-align:middle;font-size:15px;line-height:20px;color:#666666;}.slider .slider-item .content .content-container .media .media-wrapper .media-container .plain-text .container p{margin-bottom:20px;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter{text-align:left;margin-left:auto;margin-right:auto;margin-bottom:15px;clear:both;}.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter blockquote{font-size:15px;line-height:20px;color:#666666;}.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter blockquote p{font-size:28px;line-height:36px;margin-bottom:6px;padding-top:10px;background-color:#ffffff;color:#000000;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter blockquote .quote-mark{color:#666666;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter .created-at{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-256px -24px;width:24px;height:24px;overflow:hidden;margin-left:15px;display:inline-block;float:right;filter:alpha(opacity=25);-khtml-opacity:0.25;-moz-opacity:0.25;opacity:0.25;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter .created-at:hover{filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter .vcard{float:right;margin-bottom:15px;}.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter .vcard a{color:#333333;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter .vcard a:hover{text-decoration:none;}.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter .vcard a:hover .fn{text-decoration:underline;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter .vcard .fn,.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter .vcard .nickname{padding-left:42px;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter .vcard .fn{display:block;font-weight:bold;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter .vcard .nickname{margin-top:3px;display:block;color:#666666;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter .vcard .avatar{float:left;display:block;width:32px;height:32px;}.slider .slider-item .content .content-container .media .media-wrapper .media-container .twitter .vcard .avatar img{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .wikipedia{font-size:15px;line-height:20px;text-align:left;margin-left:auto;margin-right:auto;margin-bottom:15px;clear:both;}.slider .slider-item .content .content-container .media .media-wrapper .media-container .wikipedia .wiki-source{margin-bottom:15px;font-size:13px;line-height:19px;font-style:italic;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .wikipedia h4{border-bottom:1px solid #cccccc;margin-bottom:5px;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .wikipedia h4 a{color:#000000;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .wikipedia h4 a:hover{color:#0088cc;text-decoration:none;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .wikipedia p{font-size:13px;line-height:19px;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .map{line-height:normal;z-index:200;text-align:left;background-color:#ffffff;}.slider .slider-item .content .content-container .media .media-wrapper .media-container .map img{max-height:none !important;max-width:none !important;border:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .map .google-map{height:100%;width:100%;}
.slider .slider-item .content .content-container .media .media-wrapper .media-container .map .map-attribution{position:absolute;z-index:201;bottom:0px;width:100%;overflow:hidden;}.slider .slider-item .content .content-container .media .media-wrapper .media-container .map .map-attribution .attribution-text{height:19px;overflow:hidden;-webkit-user-select:none;line-height:19px;margin-right:60px;padding-left:65px;font-family:Arial,sans-serif;font-size:10px;color:#444;white-space:nowrap;color:#ffffff;text-shadow:1px 1px 1px #333333;text-align:center;}.slider .slider-item .content .content-container .media .media-wrapper .media-container .map .map-attribution .attribution-text a{color:#ffffff !important;}
.slider .slider-item .content .content-container .media .media-wrapper .credit{color:#999999;text-align:right;display:block;margin:0 auto;margin-top:6px;font-size:10px;line-height:13px;}
.slider .slider-item .content .content-container .media .media-wrapper .caption{text-align:left;margin-top:10px;color:#666666;font-size:11px;line-height:14px;text-rendering:optimizeLegibility;}
.slider .slider-item .content .media.text-media .media-wrapper .media-container{border:none;background-color:#ffffff;}
.slider .slider-item .content-container.layout-text-media .text-media{border-top:1px solid #e3e3e3;padding-top:15px;padding-right:0;}
.slider .slider-item .content-container.layout-text-media.pad-left .text-media{padding-right:15px;padding-top:0;border-right:1px solid #e3e3e3;border-top:0px solid #e3e3e3;}
.slider .slider-item .content-container.layout-text{width:100%;}.slider .slider-item .content-container.layout-text .text{width:100%;max-width:100%;}.slider .slider-item .content-container.layout-text .text .container{display:block;vertical-align:middle;text-align:left;padding:0px;width:90%;text-align:left;margin-left:auto;margin-right:auto;}
.slider .slider-item .content-container.layout-media{width:100%;}.slider .slider-item .content-container.layout-media .text{width:100%;height:100%;max-width:100%;display:block;text-align:center;}.slider .slider-item .content-container.layout-media .text .container{display:block;text-align:center;width:100%;margin-left:none;margin-right:none;}
.slider .slider-item .content-container.layout-media .media{width:100%;min-width:50%;float:none;}.slider .slider-item .content-container.layout-media .media .media-wrapper{display:block;}.slider .slider-item .content-container.layout-media .media .media-wrapper .media-container{margin-left:auto;margin-right:auto;line-height:0px;padding:0px;}
.slider .slider-item .content-container.layout-media .twitter,.slider .slider-item .content-container.layout-media .wikipedia{max-width:70%;}
.vmm-timeline .navigation{clear:both;cursor:move;width:100%;height:200px;border-top:1px solid #e3e3e3;position:relative;}.vmm-timeline .navigation .toolbar{position:absolute;top:45px;left:0px;z-index:202;background-color:#ffffff;border:1px solid #cccccc;-webkit-box-shadow:1px 1px 0px rgba(0, 0, 0, 0.2);-moz-box-shadow:1px 1px 0px rgba(0, 0, 0, 0.2);box-shadow:1px 1px 0px rgba(0, 0, 0, 0.2);}.vmm-timeline .navigation .toolbar .zoom-in,.vmm-timeline .navigation .toolbar .zoom-out,.vmm-timeline .navigation .toolbar .back-home{font-weight:normal;font-size:10px;line-height:20px;top:0px;z-index:202;width:18px;height:18px;color:#333333;text-align:center;font-weight:bold;border:1px solid #ffffff;padding:5px;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;}
.vmm-timeline .navigation .toolbar .zoom-in:hover,.vmm-timeline .navigation .toolbar .zoom-out:hover,.vmm-timeline .navigation .toolbar .back-home:hover{color:#0088cc;cursor:pointer;filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;}
.vmm-timeline .navigation .toolbar .zoom-in .icon{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-256px 0;width:24px;height:24px;}
.vmm-timeline .navigation .toolbar .zoom-out .icon{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-280px 0;width:24px;height:24px;}
.vmm-timeline .navigation .toolbar .back-home .icon{background-image:url(timeline.png);background-repeat:no-repeat;background-position:-328px 0;width:24px;height:24px;}
.vmm-timeline .navigation .timenav-background{position:absolute;cursor:move;top:0px;left:0px;height:150px;width:100%;background-color:#e9e9e9;}.vmm-timeline .navigation .timenav-background .timenav-interval-background{position:absolute;top:151px;left:0px;background:#ffffff;width:100%;height:49px;-webkit-box-shadow:-1px -1px 7px rgba(0, 0, 0, 0.1);-moz-box-shadow:-1px -1px 7px rgba(0, 0, 0, 0.1);box-shadow:-1px -1px 7px rgba(0, 0, 0, 0.1);}.vmm-timeline .navigation .timenav-background .timenav-interval-background .top-highlight{position:absolute;top:-1px;left:0px;z-index:30;width:100%;height:1px;background:#ffffff;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;-webkit-box-shadow:1px 1px 5px rgba(0, 0, 0, 0.2);-moz-box-shadow:1px 1px 5px rgba(0, 0, 0, 0.2);box-shadow:1px 1px 5px rgba(0, 0, 0, 0.2);}
.vmm-timeline .navigation .timenav-background .timenav-line{position:absolute;top:0px;left:50%;width:3px;height:150px;background:#0088cc;z-index:201;-webkit-box-shadow:1px 1px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:1px 1px 7px rgba(0, 0, 0, 0.3);box-shadow:1px 1px 7px rgba(0, 0, 0, 0.3);}
.vmm-timeline .navigation .timenav-background .timenav-indicator{position:absolute;top:-1px;left:50%;z-index:202;background-image:url(timeline.png);background-repeat:no-repeat;background-position:-160px -48px;width:24px;height:24px;}
.vmm-timeline .navigation .timenav-background .timenav-tag{height:50px;}.vmm-timeline .navigation .timenav-background .timenav-tag div{height:50px;display:table;}.vmm-timeline .navigation .timenav-background .timenav-tag div h3{display:table-cell;vertical-align:middle;padding-left:65px;font-size:15px;color:#9d9d9d;}
.vmm-timeline .navigation .timenav-background .timenav-tag-row-2{background:#f1f1f1;}
.vmm-timeline .navigation .timenav-background .timenav-tag-row-3{background:#e9e9e9;}
.vmm-timeline .navigation .timenav{position:absolute;top:0px;left:-250px;z-index:1;}.vmm-timeline .navigation .timenav .content{position:relative;}.vmm-timeline .navigation .timenav .content .marker.start{display:none;}
.vmm-timeline .navigation .timenav .content .marker.active .dot{background:#0088cc;z-index:200;}
.vmm-timeline .navigation .timenav .content .marker.active .line{z-index:199;background:#0088cc;width:1px;}.vmm-timeline .navigation .timenav .content .marker.active .line .event-line{background:#0088cc;filter:alpha(opacity=75);-khtml-opacity:0.75;-moz-opacity:0.75;opacity:0.75;}
.vmm-timeline .navigation .timenav .content .marker.active .flag{z-index:200;background-image:url(timeline.png);background-repeat:no-repeat;background-position:0 -53px;width:153px;height:53px;}.vmm-timeline .navigation .timenav .content .marker.active .flag .flag-content h3{color:#0088cc;}
.vmm-timeline .navigation .timenav .content .marker.active .flag .flag-content .thumbnail{opacity:1;-moz-opacity:1;filter:alpha(opacity=100);}
.vmm-timeline .navigation .timenav .content .marker.active .flag.row1,.vmm-timeline .navigation .timenav .content .marker.active .flag.row2,.vmm-timeline .navigation .timenav .content .marker.active .flag.row3{z-index:200;}
.vmm-timeline .navigation .timenav .content .marker.active:hover{cursor:default;}.vmm-timeline .navigation .timenav .content .marker.active:hover .flag .flag-content h3{color:#0088cc;}
.vmm-timeline .navigation .timenav .content .marker.active:hover .flag .flag-content h4{color:#999999;}
.vmm-timeline .navigation .timenav .content .marker:hover .line{z-index:24;background:#999999;}
.vmm-timeline .navigation .timenav .content .marker{position:absolute;top:0px;left:150px;display:block;}.vmm-timeline .navigation .timenav .content .marker .dot{position:absolute;top:150px;left:0px;display:block;width:6px;height:6px;background:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;z-index:21;}
.vmm-timeline .navigation .timenav .content .marker .line{position:absolute;top:0px;left:3px;width:1px;height:150px;background:#cccccc;-moz-box-shadow:1px 0 0 #ffffff;-webkit-box-shadow:1px 0 0 #ffffff;box-shadow:1px 0 0 #ffffff;z-index:22;}.vmm-timeline .navigation .timenav .content .marker .line .event-line{position:absolute;z-index:22;left:0px;height:1px;width:1px;background:#0088cc;filter:alpha(opacity=15);-khtml-opacity:0.15;-moz-opacity:0.15;opacity:0.15;}
.vmm-timeline .navigation .timenav .content .marker .flag{position:absolute;top:15px;left:3px;padding:0px;display:block;z-index:23;width:153px;height:56px;background-image:url(timeline.png);background-repeat:no-repeat;background-position:0 0;width:153px;height:53px;}.vmm-timeline .navigation .timenav .content .marker .flag .flag-content{padding:0px 7px 2px 6px;overflow:hidden;height:36px;}.vmm-timeline .navigation .timenav .content .marker .flag .flag-content h3{font-weight:bold;font-size:15px;line-height:20px;font-size:11px;line-height:11px;color:#999999;margin-top:5px;margin-bottom:2px;}.vmm-timeline .navigation .timenav .content .marker .flag .flag-content h3 small{display:none;}
.vmm-timeline .navigation .timenav .content .marker .flag .flag-content h4{display:none;font-weight:normal;font-size:15px;line-height:20px;margin-top:5px;font-size:10px;line-height:10px;color:#aaaaaa;}.vmm-timeline .navigation .timenav .content .marker .flag .flag-content h4 small{display:none;}
.vmm-timeline .navigation .timenav .content .marker .flag .flag-content .thumbnail{margin-bottom:15px;margin-right:3px;opacity:0.5;-moz-opacity:0.5;filter:alpha(opacity=50);}.vmm-timeline .navigation .timenav .content .marker .flag .flag-content .thumbnail img{width:22px;height:22px;max-height:none;max-width:none;border:0;border:1px solid #999999;padding:0;margin:0;}
.vmm-timeline .navigation .timenav .content .marker .flag:hover{cursor:pointer;background-image:url(timeline.png);background-repeat:no-repeat;background-position:0 -53px;width:153px;height:53px;}.vmm-timeline .navigation .timenav .content .marker .flag:hover .flag-content h3{color:#333333;}
.vmm-timeline .navigation .timenav .content .marker .flag:hover .flag-content h4{color:#aaaaaa;}
.vmm-timeline .navigation .timenav .content .marker .flag:hover .flag-content .thumbnail{opacity:1;-moz-opacity:1;filter:alpha(opacity=100);}
.vmm-timeline .navigation .timenav .content .marker .flag.row1{z-index:25;top:48px;}
.vmm-timeline .navigation .timenav .content .marker .flag.row2{z-index:24;top:96px;}
.vmm-timeline .navigation .timenav .content .marker .flag.row3{z-index:23;top:1px;}
.vmm-timeline .navigation .timenav .content .marker .flag.zFront{z-index:201;}
.vmm-timeline .navigation .timenav .content .era{position:absolute;top:138px;left:150px;height:12px;display:block;background:#0088cc;overflow:hidden;border-left:1px solid #cccccc;border-right:1px solid #cccccc;border-top:1px solid #cccccc;filter:alpha(opacity=75);-khtml-opacity:0.75;-moz-opacity:0.75;opacity:0.75;-moz-border-radius-topleft:7px;-webkit-border-top-left-radius:7px;border-top-left-radius:7px;-moz-border-radius-topright:7px;-webkit-border-top-right-radius:7px;border-top-right-radius:7px;z-index:-10;}.vmm-timeline .navigation .timenav .content .era h3{font-weight:bold;font-size:10px;line-height:10px;color:#ffffff;position:absolute;top:-1px;left:9px;}
.vmm-timeline .navigation .timenav .time{position:absolute;left:0px;top:150px;height:50px;background-color:#ffffff;}.vmm-timeline .navigation .timenav .time .time-interval-minor{max-width:none;height:6px;white-space:nowrap;position:absolute;top:-9px;left:8px;z-index:10;}.vmm-timeline .navigation .timenav .time .time-interval-minor .minor{position:relative;top:2px;display:inline-block;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAMCAMAAACdvocfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRFzMzM////040VdgAAAAJ0Uk5T/wDltzBKAAAAEklEQVR42mJgYAQCBopJgAADAAbwADHy2qHzAAAAAElFTkSuQmCC);width:100px;height:6px;background-position:center top;white-space:nowrap;color:#666666;margin-top:0px;padding-top:0px;}
.vmm-timeline .navigation .timenav .time .time-interval{white-space:nowrap;position:absolute;top:5px;left:0px;}.vmm-timeline .navigation .timenav .time .time-interval div{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAMCAMAAACdvocfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRFzMzM////040VdgAAAAJ0Uk5T/wDltzBKAAAAEklEQVR42mJgYAQCBopJgAADAAbwADHy2qHzAAAAAElFTkSuQmCC);background-position:left top;background-repeat:no-repeat;padding-top:3px;position:absolute;height:3px;left:0px;display:block;font-weight:normal;font-size:10px;line-height:20px;text-transform:uppercase;text-align:left;text-indent:0px;white-space:nowrap;color:#666666;margin-left:0px;margin-right:0px;margin-top:1px;z-index:2;}.vmm-timeline .navigation .timenav .time .time-interval div strong{font-weight:bold;color:#000000;}
.vmm-timeline .navigation .timenav .time .time-interval-major{white-space:nowrap;position:absolute;top:5px;left:0px;}.vmm-timeline .navigation .timenav .time .time-interval-major div{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAMCAMAAACdvocfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRFzMzM////040VdgAAAAJ0Uk5T/wDltzBKAAAAEklEQVR42mJgYAQCBopJgAADAAbwADHy2qHzAAAAAElFTkSuQmCC);background-position:left top;background-repeat:no-repeat;padding-top:15px;position:absolute;height:15px;left:0px;display:block;font-weight:bold;font-size:12px;line-height:20px;text-transform:uppercase;text-align:left;text-indent:0px;white-space:nowrap;color:#333333;margin-left:0px;margin-right:0px;margin-top:1px;z-index:5;}.vmm-timeline .navigation .timenav .time .time-interval-major div strong{font-weight:bold;color:#000000;}
.vmm-timeline{font-size:15px;font-weight:normal;line-height:20px;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;}.vmm-timeline p{font-size:15px;font-weight:normal;line-height:20px;margin-bottom:20px;color:#666666;}.vmm-timeline p small{font-size:12px;line-height:17px;}
.vmm-timeline p:first-child{margin-top:20px;}
.vmm-timeline .navigation p{color:#999999;}
.vmm-timeline .feature h3,.vmm-timeline .feature h4,.vmm-timeline .feature h5,.vmm-timeline .feature h6{margin-bottom:15px;}
.vmm-timeline .feature p{color:#666666;}
.vmm-timeline .date a,.vmm-timeline .title a{color:#999999;}
.vmm-timeline .hyphenate{-webkit-hyphens:auto;-moz-hyphens:auto;-ms-hyphens:auto;hyphens:auto;word-wrap:break-word;}
.vmm-timeline h1,.vmm-timeline h2,.vmm-timeline h3,.vmm-timeline h4,.vmm-timeline h5,.vmm-timeline h6{font-weight:normal;color:#000000;text-transform:none;}.vmm-timeline h1 a,.vmm-timeline h2 a,.vmm-timeline h3 a,.vmm-timeline h4 a,.vmm-timeline h5 a,.vmm-timeline h6 a{color:#999999;}
.vmm-timeline h1 small,.vmm-timeline h2 small,.vmm-timeline h3 small,.vmm-timeline h4 small,.vmm-timeline h5 small,.vmm-timeline h6 small{color:#999999;}
.vmm-timeline h1.date,.vmm-timeline h2.date,.vmm-timeline h3.date,.vmm-timeline h4.date,.vmm-timeline h5.date,.vmm-timeline h6.date{font-weight:bold;}
.vmm-timeline h2.start{font-size:36px;line-height:38px;margin-bottom:15px;}
.vmm-timeline h1{margin-bottom:15px;font-size:32px;line-height:34px;}.vmm-timeline h1 small{font-size:18px;}
.vmm-timeline h2{margin-bottom:15px;font-size:28px;line-height:30px;}.vmm-timeline h2 small{font-size:14px;line-height:16px;}
.vmm-timeline h2.date{font-size:16px;line-height:18px;margin-bottom:3.75px;color:#999999;}
.vmm-timeline h3,.vmm-timeline h4,.vmm-timeline h5,.vmm-timeline h6{line-height:40px;}.vmm-timeline h3 .active,.vmm-timeline h4 .active,.vmm-timeline h5 .active,.vmm-timeline h6 .active{color:#0088cc;}
.vmm-timeline h3{font-size:28px;line-height:30px;}.vmm-timeline h3 small{font-size:14px;}
.vmm-timeline h4{font-size:16px;line-height:18px;}.vmm-timeline h4 small{font-size:12px;}
.vmm-timeline h5{font-size:14px;line-height:16px;}
.vmm-timeline h6{font-size:13px;line-height:14px;text-transform:uppercase;}
.vmm-timeline strong{font-weight:bold;}
.vmm-timeline Q{quotes:'„' '“';font-style:italic;}
.vmm-timeline blockquote{font-size:28px;text-align:left;line-height:36px;margin-bottom:6px;padding-top:10px;background-color:#ffffff;color:#000000;}
.vmm-timeline .credit{color:#999999;text-align:right;font-size:10px;line-height:10px;display:block;margin:0 auto;clear:both;}
.vmm-timeline .caption{text-align:left;margin-top:5px;color:#666666;font-size:11px;line-height:14px;clear:both;}
.tooltip{position:absolute;z-index:205;display:block;visibility:visible;padding:5px;opacity:0;filter:alpha(opacity=0);font-size:15px;font-weight:bold;line-height:20px;font-size:12px;line-height:12px;}
.tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
.tooltip.top{margin-top:-2px;}
.tooltip.right{margin-left:2px;}
.tooltip.bottom{margin-top:2px;}
.tooltip.left{margin-left:-2px;}
.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.tooltip-arrow{position:absolute;width:0;height:0;}
@media only screen and (max-width:480px),only screen and (max-device-width:480px){}@media (max-width:600px){.slider .slider-item .content .content-container{display:block;}.slider .slider-item .content .content-container .text{width:100%;max-width:100%;min-width:120px;display:block;}.slider .slider-item .content .content-container .text .container{display:block;-webkit-hyphens:auto;-moz-hyphens:auto;-ms-hyphens:auto;hyphens:auto;word-wrap:break-word;} .slider .slider-item .content .content-container .media{width:100%;min-width:50%;float:none;}.slider .slider-item .content .content-container .media .media-wrapper{margin-left:0px;margin-right:0px;width:100%;display:block;} .slider .slider-item .content .content-container .media.text-media .media-wrapper .media-container{border:none;background-color:#ffffff;}}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

File diff suppressed because it is too large Load Diff

365
vendor/timeline/LICENSE vendored Normal file
View File

@ -0,0 +1,365 @@
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
-------------------------------------------
Map tiles by [Stamen Design](http://stamen.com "Stamen Design"), under
[CC BY 3.0](http://creativecommons.org/licenses/by/3.0 "CC BY 3.0").
Data by [OpenStreetMap](http://openstreetmap.org "OpenStreetMap"),
under [CC BY SA](http://creativecommons.org/licenses/by-sa/3.0 "CC BY SA").

1
vendor/timeline/README vendored Normal file
View File

@ -0,0 +1 @@
Verite TimelineJS v2.25

BIN
vendor/timeline/css/loading.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

284
vendor/timeline/css/timeline.css vendored Normal file
View File

@ -0,0 +1,284 @@
.vco-storyjs{}.vco-storyjs div *{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
.vco-storyjs h1,.vco-storyjs h2,.vco-storyjs h3,.vco-storyjs h4,.vco-storyjs h5,.vco-storyjs h6,.vco-storyjs p,.vco-storyjs blockquote,.vco-storyjs pre,.vco-storyjs a,.vco-storyjs abbr,.vco-storyjs acronym,.vco-storyjs address,.vco-storyjs cite,.vco-storyjs code,.vco-storyjs del,.vco-storyjs dfn,.vco-storyjs em,.vco-storyjs img,.vco-storyjs q,.vco-storyjs s,.vco-storyjs samp,.vco-storyjs small,.vco-storyjs strike,.vco-storyjs strong,.vco-storyjs sub,.vco-storyjs sup,.vco-storyjs tt,.vco-storyjs var,.vco-storyjs dd,.vco-storyjs dl,.vco-storyjs dt,.vco-storyjs li,.vco-storyjs ol,.vco-storyjs ul,.vco-storyjs fieldset,.vco-storyjs form,.vco-storyjs label,.vco-storyjs legend,.vco-storyjs button,.vco-storyjs table,.vco-storyjs caption,.vco-storyjs tbody,.vco-storyjs tfoot,.vco-storyjs thead,.vco-storyjs tr,.vco-storyjs th,.vco-storyjs td,.vco-storyjs .vco-container,.vco-storyjs .content-container,.vco-storyjs .media,.vco-storyjs .text,.vco-storyjs .vco-slider,.vco-storyjs .slider,.vco-storyjs .date,.vco-storyjs .title,.vco-storyjs .messege,.vco-storyjs .map,.vco-storyjs .credit,.vco-storyjs .caption,.vco-storyjs .vco-feedback,.vco-storyjs .vco-feature,.vco-storyjs .toolbar,.vco-storyjs .marker,.vco-storyjs .dot,.vco-storyjs .line,.vco-storyjs .flag,.vco-storyjs .time,.vco-storyjs .era,.vco-storyjs .major,.vco-storyjs .minor,.vco-storyjs .vco-navigation,.vco-storyjs .start,.vco-storyjs .active{margin:0;padding:0;border:0;font-weight:normal;font-style:normal;font-size:100%;line-height:1;font-family:inherit;width:auto;float:none;}
.vco-storyjs h1,.vco-storyjs h2,.vco-storyjs h3,.vco-storyjs h4,.vco-storyjs h5,.vco-storyjs h6{clear:none;}
.vco-storyjs table{border-collapse:collapse;border-spacing:0;}
.vco-storyjs ol,.vco-storyjs ul{list-style:none;}
.vco-storyjs q:before,.vco-storyjs q:after,.vco-storyjs blockquote:before,.vco-storyjs blockquote:after{content:"";}
.vco-storyjs a:focus{outline:thin dotted;}
.vco-storyjs a:hover,.vco-storyjs a:active{outline:0;}
.vco-storyjs article,.vco-storyjs aside,.vco-storyjs details,.vco-storyjs figcaption,.vco-storyjs figure,.vco-storyjs footer,.vco-storyjs header,.vco-storyjs hgroup,.vco-storyjs nav,.vco-storyjs section{display:block;}
.vco-storyjs audio,.vco-storyjs canvas,.vco-storyjs video{display:inline-block;*display:inline;*zoom:1;}
.vco-storyjs audio:not([controls]){display:none;}
.vco-storyjs div{max-width:none;}
.vco-storyjs sub,.vco-storyjs sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}
.vco-storyjs sup{top:-0.5em;}
.vco-storyjs sub{bottom:-0.25em;}
.vco-storyjs img{border:0;-ms-interpolation-mode:bicubic;}
.vco-storyjs button,.vco-storyjs input,.vco-storyjs select,.vco-storyjs textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;}
.vco-storyjs button,.vco-storyjs input{line-height:normal;*overflow:visible;}
.vco-storyjs button::-moz-focus-inner,.vco-storyjs input::-moz-focus-inner{border:0;padding:0;}
.vco-storyjs button,.vco-storyjs input[type="button"],.vco-storyjs input[type="reset"],.vco-storyjs input[type="submit"]{cursor:pointer;-webkit-appearance:button;}
.vco-storyjs input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;}
.vco-storyjs input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}
.vco-storyjs textarea{overflow:auto;vertical-align:top;}
.vco-storyjs{font-family:"Georgia",Times New Roman,Times,serif;}.vco-storyjs .twitter,.vco-storyjs .vcard,.vco-storyjs .messege,.vco-storyjs .credit,.vco-storyjs .caption,.vco-storyjs .zoom-in,.vco-storyjs .zoom-out,.vco-storyjs .back-home,.vco-storyjs .time-interval div,.vco-storyjs .time-interval-major div,.vco-storyjs .nav-container{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif !important;}
.vco-storyjs h1.date,.vco-storyjs h2.date,.vco-storyjs h3.date,.vco-storyjs h4.date,.vco-storyjs h5.date,.vco-storyjs h6.date{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif !important;}
.vco-storyjs .timenav h1,.vco-storyjs .flag-content h1,.vco-storyjs .era h1,.vco-storyjs .timenav h2,.vco-storyjs .flag-content h2,.vco-storyjs .era h2,.vco-storyjs .timenav h3,.vco-storyjs .flag-content h3,.vco-storyjs .era h3,.vco-storyjs .timenav h4,.vco-storyjs .flag-content h4,.vco-storyjs .era h4,.vco-storyjs .timenav h5,.vco-storyjs .flag-content h5,.vco-storyjs .era h5,.vco-storyjs .timenav h6,.vco-storyjs .flag-content h6,.vco-storyjs .era h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif !important;}
.vco-storyjs p,.vco-storyjs blockquote,.vco-storyjs blockquote p,.vco-storyjs .twitter blockquote p{font-family:"Georgia",Times New Roman,Times,serif !important;}
.vco-storyjs .vco-feature h1,.vco-storyjs .vco-feature h2,.vco-storyjs .vco-feature h3,.vco-storyjs .vco-feature h4,.vco-storyjs .vco-feature h5,.vco-storyjs .vco-feature h6{font-family:"Georgia",Times New Roman,Times,serif;}
.timeline-tooltip{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;}
.thumbnail{background-image:url(timeline.png?v4.4);}
@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2){.thumbnail{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;}}.vco-storyjs{font-size:15px;font-weight:normal;line-height:20px;-webkit-font-smoothing:antialiased;-webkit-text-size-adjust:100%;}.vco-storyjs p{font-size:15px;font-weight:normal;line-height:20px;margin-bottom:20px;color:#666666;}.vco-storyjs p small{font-size:12px;line-height:17px;}
.vco-storyjs p:first-child{margin-top:20px;}
.vco-storyjs .vco-navigation p{color:#999999;}
.vco-storyjs .vco-feature h3,.vco-storyjs .vco-feature h4,.vco-storyjs .vco-feature h5,.vco-storyjs .vco-feature h6{margin-bottom:15px;}
.vco-storyjs .vco-feature p{color:#666666;}
.vco-storyjs .vco-feature blockquote,.vco-storyjs .vco-feature blockquote p{color:#000000;}
.vco-storyjs .date a,.vco-storyjs .title a{color:#999999;}
.vco-storyjs .hyphenate{-webkit-hyphens:auto;-moz-hyphens:auto;-ms-hyphens:auto;hyphens:auto;word-wrap:break-word;}
.vco-storyjs h1,.vco-storyjs h2,.vco-storyjs h3,.vco-storyjs h4,.vco-storyjs h5,.vco-storyjs h6{font-weight:normal;color:#000000;text-transform:none;}.vco-storyjs h1 a,.vco-storyjs h2 a,.vco-storyjs h3 a,.vco-storyjs h4 a,.vco-storyjs h5 a,.vco-storyjs h6 a{color:#999999;}
.vco-storyjs h1 small,.vco-storyjs h2 small,.vco-storyjs h3 small,.vco-storyjs h4 small,.vco-storyjs h5 small,.vco-storyjs h6 small{color:#999999;}
.vco-storyjs h1.date,.vco-storyjs h2.date,.vco-storyjs h3.date,.vco-storyjs h4.date,.vco-storyjs h5.date,.vco-storyjs h6.date{font-weight:bold;}
.vco-storyjs h2.start{font-size:36px;line-height:38px;margin-bottom:15px;}
.vco-storyjs h1{margin-bottom:15px;font-size:32px;line-height:34px;}.vco-storyjs h1 small{font-size:18px;}
.vco-storyjs h2{margin-bottom:15px;font-size:28px;line-height:30px;}.vco-storyjs h2 small{font-size:14px;line-height:16px;}
.vco-storyjs h2.date{font-size:16px;line-height:18px;margin-bottom:3.75px;color:#999999;}
.vco-storyjs h3,.vco-storyjs h4,.vco-storyjs h5,.vco-storyjs h6{line-height:40px;}.vco-storyjs h3 .active,.vco-storyjs h4 .active,.vco-storyjs h5 .active,.vco-storyjs h6 .active{color:#0088cc;}
.vco-storyjs h3{font-size:28px;line-height:30px;}.vco-storyjs h3 small{font-size:14px;}
.vco-storyjs h4{font-size:20px;line-height:22px;}.vco-storyjs h4 small{font-size:12px;}
.vco-storyjs h5{font-size:16px;line-height:18px;}
.vco-storyjs h6{font-size:13px;line-height:14px;text-transform:uppercase;}
.vco-storyjs strong{font-weight:bold;font-style:inherit;}
.vco-storyjs em{font-style:italic;font-weight:inherit;}
.vco-storyjs Q{quotes:'„' '“';font-style:italic;}
.vco-storyjs blockquote,.vco-storyjs blockquote p{font-size:24px;line-height:32px;text-align:left;margin-bottom:6px;padding-top:10px;background-color:#ffffff;color:#000000;}
.vco-storyjs .credit{color:#999999;text-align:right;font-size:10px;line-height:10px;display:block;margin:0 auto;clear:both;}
.vco-storyjs .caption{text-align:left;margin-top:5px;color:#666666;font-size:11px;line-height:14px;clear:both;}
.vco-storyjs.vco-right-to-left h1,.vco-storyjs.vco-right-to-left h2,.vco-storyjs.vco-right-to-left h3,.vco-storyjs.vco-right-to-left h4,.vco-storyjs.vco-right-to-left h5,.vco-storyjs.vco-right-to-left h6,.vco-storyjs.vco-right-to-left p,.vco-storyjs.vco-right-to-left blockquote,.vco-storyjs.vco-right-to-left pre,.vco-storyjs.vco-right-to-left a,.vco-storyjs.vco-right-to-left abbr,.vco-storyjs.vco-right-to-left acronym,.vco-storyjs.vco-right-to-left address,.vco-storyjs.vco-right-to-left cite,.vco-storyjs.vco-right-to-left code,.vco-storyjs.vco-right-to-left del,.vco-storyjs.vco-right-to-left dfn,.vco-storyjs.vco-right-to-left em,.vco-storyjs.vco-right-to-left img,.vco-storyjs.vco-right-to-left q,.vco-storyjs.vco-right-to-left s,.vco-storyjs.vco-right-to-left samp,.vco-storyjs.vco-right-to-left small,.vco-storyjs.vco-right-to-left strike,.vco-storyjs.vco-right-to-left strong,.vco-storyjs.vco-right-to-left sub,.vco-storyjs.vco-right-to-left sup,.vco-storyjs.vco-right-to-left tt,.vco-storyjs.vco-right-to-left var,.vco-storyjs.vco-right-to-left dd,.vco-storyjs.vco-right-to-left dl,.vco-storyjs.vco-right-to-left dt,.vco-storyjs.vco-right-to-left li,.vco-storyjs.vco-right-to-left ol,.vco-storyjs.vco-right-to-left ul,.vco-storyjs.vco-right-to-left fieldset,.vco-storyjs.vco-right-to-left form,.vco-storyjs.vco-right-to-left label,.vco-storyjs.vco-right-to-left legend,.vco-storyjs.vco-right-to-left button,.vco-storyjs.vco-right-to-left table,.vco-storyjs.vco-right-to-left caption,.vco-storyjs.vco-right-to-left tbody,.vco-storyjs.vco-right-to-left tfoot,.vco-storyjs.vco-right-to-left thead,.vco-storyjs.vco-right-to-left tr,.vco-storyjs.vco-right-to-left th,.vco-storyjs.vco-right-to-left td{direction:rtl;}
.timeline-tooltip{position:absolute;z-index:205;display:block;visibility:visible;padding:5px;opacity:0;filter:alpha(opacity=0);font-size:15px;font-weight:bold;line-height:20px;font-size:12px;line-height:12px;}
.timeline-tooltip.in{opacity:0.8;filter:alpha(opacity=80);}
.timeline-tooltip.top{margin-top:-2px;}
.timeline-tooltip.right{margin-left:2px;}
.timeline-tooltip.bottom{margin-top:2px;}
.timeline-tooltip.left{margin-left:-2px;}
.timeline-tooltip.top .timeline-tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;}
.timeline-tooltip.left .timeline-tooltip-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;}
.timeline-tooltip.bottom .timeline-tooltip-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;}
.timeline-tooltip.right .timeline-tooltip-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;}
.timeline-tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;text-decoration:none;background-color:#000000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}
.timeline-tooltip-arrow{position:absolute;width:0;height:0;}
@media only screen and (max-width:480px),only screen and (max-device-width:480px){.vco-slider .nav-next,.vco-slider .nav-previous{display:none;}}@media (max-width:640px){}.vco-skinny .vco-slider .slider-item .content .layout-text-media .text .container{text-align:center !important;}
.vco-skinny .vco-slider .slider-item .content .layout-text-media h2,.vco-skinny .vco-slider .slider-item .content .layout-text-media h3{display:block !important;width:100% !important;text-align:center !important;}
.vco-skinny .vco-slider .slider-item .content .content-container{display:block;}.vco-skinny .vco-slider .slider-item .content .content-container .text{width:100%;max-width:100%;min-width:120px;display:block;}.vco-skinny .vco-slider .slider-item .content .content-container .text .container{display:block;-webkit-hyphens:auto;-moz-hyphens:auto;-ms-hyphens:auto;hyphens:auto;word-wrap:break-word;}
.vco-skinny .vco-slider .slider-item .content .content-container .media{width:100%;min-width:50%;float:none;}.vco-skinny .vco-slider .slider-item .content .content-container .media .media-wrapper{margin-left:0px;margin-right:0px;width:100%;display:block;}
.vco-skinny.vco-notouch .vco-slider .nav-previous,.vco-skinny.vco-notouch .vco-slider .nav-next{z-index:203;}.vco-skinny.vco-notouch .vco-slider .nav-previous .nav-container .date,.vco-skinny.vco-notouch .vco-slider .nav-next .nav-container .date,.vco-skinny.vco-notouch .vco-slider .nav-previous .nav-container .title,.vco-skinny.vco-notouch .vco-slider .nav-next .nav-container .title{filter:alpha(opacity=1);-khtml-opacity:0.01;-moz-opacity:0.01;opacity:0.01;}
.vco-skinny.vco-notouch .vco-slider .nav-previous .nav-container .icon,.vco-skinny.vco-notouch .vco-slider .nav-next .nav-container .icon{filter:alpha(opacity=15);-khtml-opacity:0.15;-moz-opacity:0.15;opacity:0.15;}
.vco-skinny.vco-notouch .vco-slider .nav-previous .icon{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-208px 0;width:24px;height:24px;overflow:hidden;margin-left:10px;}
.vco-skinny.vco-notouch .vco-slider .nav-next .icon{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-232px 0;width:24px;height:24px;overflow:hidden;margin-left:66px;}
.vco-skinny.vco-notouch .vco-slider .nav-previous:hover,.vco-skinny.vco-notouch .vco-slider .nav-next:hover{color:#aaaaaa !important;background-color:#333333;background-color:rgba(0, 0, 0, 0.65);-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;}.vco-skinny.vco-notouch .vco-slider .nav-previous:hover .nav-container .icon,.vco-skinny.vco-notouch .vco-slider .nav-next:hover .nav-container .icon,.vco-skinny.vco-notouch .vco-slider .nav-previous:hover .nav-container .date,.vco-skinny.vco-notouch .vco-slider .nav-next:hover .nav-container .date,.vco-skinny.vco-notouch .vco-slider .nav-previous:hover .nav-container .title,.vco-skinny.vco-notouch .vco-slider .nav-next:hover .nav-container .title{-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;font-weight:bold;filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;}
.vco-skinny.vco-notouch .vco-slider .nav-previous:hover .nav-container .title,.vco-skinny.vco-notouch .vco-slider .nav-next:hover .nav-container .title{padding-bottom:5px;}
.vco-skinny.vco-notouch .vco-slider .nav-previous:hover .nav-container .date,.vco-skinny.vco-notouch .vco-slider .nav-next:hover .nav-container .date,.vco-skinny.vco-notouch .vco-slider .nav-previous:hover .nav-container .title,.vco-skinny.vco-notouch .vco-slider .nav-next:hover .nav-container .title{padding-left:5px;padding-right:5px;}
@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2){.vco-skinny.vco-notouch .vco-slider .nav-previous .icon{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:-208px 0;width:24px;height:24px;overflow:hidden;} .vco-skinny.vco-notouch .vco-slider .nav-next .icon{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:-232px 0;width:24px;height:24px;overflow:hidden;}}.vco-slider{width:100%;height:100%;overflow:hidden;}.vco-slider .slider-container-mask{text-align:center;width:100%;height:100%;overflow:hidden;}.vco-slider .slider-container-mask .slider-container{position:absolute;top:0px;left:-2160px;width:100%;height:100%;text-align:center;display:block;}.vco-slider .slider-container-mask .slider-container .slider-item-container{display:table-cell;vertical-align:middle;}
.vco-notouch .vco-slider .nav-previous:hover,.vco-notouch .vco-slider .nav-next:hover{color:#333333;cursor:pointer;}
.vco-notouch .vco-slider .nav-previous:hover .icon{margin-left:10px;}
.vco-notouch .vco-slider .nav-next:hover .icon{margin-left:66px;}
.vco-notouch .vco-slider .slider-item .content .content-container .media .media-container .wikipedia h4 a:hover{color:#0088cc;text-decoration:none;}
.vco-notouch .vco-slider .slider-item .content .content-container .created-at:hover{filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;}
.vco-notouch .vco-slider .slider-item .content .content-container .googleplus .googleplus-content .googleplus-attachments a:hover{text-decoration:none;}.vco-notouch .vco-slider .slider-item .content .content-container .googleplus .googleplus-content .googleplus-attachments a:hover h5{text-decoration:underline;}
.vco-slider img,.vco-slider embed,.vco-slider object,.vco-slider video,.vco-slider iframe{max-width:100%;}
.vco-slider .nav-previous,.vco-slider .nav-next{position:absolute;top:0px;width:100px;color:#DBDBDB;font-size:11px;}.vco-slider .nav-previous .nav-container,.vco-slider .nav-next .nav-container{height:100px;width:100px;position:absolute;}
.vco-slider .nav-previous .icon,.vco-slider .nav-next .icon{margin-top:12px;margin-bottom:15px;}
.vco-slider .nav-previous .date,.vco-slider .nav-next .date,.vco-slider .nav-previous .title,.vco-slider .nav-next .title{line-height:14px;}.vco-slider .nav-previous .date a,.vco-slider .nav-next .date a,.vco-slider .nav-previous .title a,.vco-slider .nav-next .title a{color:#999999;}
.vco-slider .nav-previous .date small,.vco-slider .nav-next .date small,.vco-slider .nav-previous .title small,.vco-slider .nav-next .title small{display:none;}
.vco-slider .nav-previous .date,.vco-slider .nav-next .date{font-size:13px;line-height:13px;font-weight:bold;text-transform:uppercase;margin-bottom:5px;}
.vco-slider .nav-previous .title,.vco-slider .nav-next .title{font-size:11px;line-height:13px;}
.vco-slider .nav-previous{float:left;text-align:left;}.vco-slider .nav-previous .icon{margin-left:15px;background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-160px 0;width:24px;height:24px;overflow:hidden;}
.vco-slider .nav-previous .date,.vco-slider .nav-previous .title{text-align:left;padding-left:15px;}
.vco-slider .nav-next{float:right;text-align:right;}.vco-slider .nav-next .icon{margin-left:61px;background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-184px 0;width:24px;height:24px;overflow:hidden;}
.vco-slider .nav-next .date,.vco-slider .nav-next .title{text-align:right;padding-right:15px;}
@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2){.vco-slider .nav-previous .icon{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:-160px 0;width:24px;height:24px;overflow:hidden;} .vco-slider .nav-next .icon{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:-184px 0;width:24px;height:24px;overflow:hidden;}}.vco-slider .slider-item{position:absolute;width:700px;height:100%;padding:0px;margin:0px;display:table;overflow-y:auto;}.vco-slider .slider-item .content{display:table-cell;vertical-align:middle;}.vco-slider .slider-item .content .pad-top .text .container{padding-top:15px;}
.vco-slider .slider-item .content .pad-right .text .container{padding-right:15px;}
.vco-slider .slider-item .content .pad-left .text .container{padding-left:30px;}
.vco-slider .slider-item .content .pad-left .media.text-media .media-wrapper .media-container{border:none;background-color:#ffffff;}
.vco-slider .slider-item .content .content-container{display:table;vertical-align:middle;}.vco-slider .slider-item .content .content-container .text{width:40%;max-width:50%;min-width:120px;display:table-cell;vertical-align:middle;}.vco-slider .slider-item .content .content-container .text .container{display:table-cell;vertical-align:middle;text-align:left;}.vco-slider .slider-item .content .content-container .text .container p{-webkit-hyphens:auto;-moz-hyphens:auto;-ms-hyphens:auto;hyphens:auto;word-wrap:break-word;}
.vco-slider .slider-item .content .content-container .text .container h2.date{font-size:15px;line-height:15px;font-weight:normal;}
.vco-slider .slider-item .content .content-container .text .container .slide-tag{font-size:11px;font-weight:bold;color:#ffffff;background-color:#cccccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;vertical-align:baseline;white-space:nowrap;line-height:11px;padding:1px 3px 1px;margin-left:7.5px;margin-bottom:7.5px;}
.vco-slider .slider-item .content .content-container .media{width:100%;min-width:50%;float:left;}.vco-slider .slider-item .content .content-container .media .media-wrapper{display:inline-block;margin-left:auto;margin-right:auto;}.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container{display:inline-block;line-height:0px;padding:0px;max-height:100%;}.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .media-frame,.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .media-image img{border:1px solid;border-color:#cccccc #999999 #999999 #cccccc;background-color:#ffffff;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .media-frame iframe{background-color:#ffffff;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .soundcloud{border:0;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .media-image{display:inline-block;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .media-shadow{position:relative;z-index:1;background:#ffffff;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .media-shadow:before,.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .media-shadow:after{z-index:-1;position:absolute;content:"";bottom:15px;left:10px;width:50%;top:80%;max-width:300px;background:#999999;-webkit-box-shadow:0 15px 10px #999999;-moz-box-shadow:0 15px 10px #999999;box-shadow:0 15px 10px #999999;-webkit-transform:rotate(-2deg);-moz-transform:rotate(-2deg);-ms-transform:rotate(-2deg);-o-transform:rotate(-2deg);transform:rotate(-2deg);}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .media-shadow::after{-webkit-transform:rotate(2deg);-moz-transform:rotate(2deg);-ms-transform:rotate(2deg);-o-transform:rotate(2deg);transform:rotate(2deg);right:10px;left:auto;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .plain-text{display:table;}.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .plain-text .container{display:table-cell;vertical-align:middle;font-size:15px;line-height:20px;color:#666666;}.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .plain-text .container p{margin-bottom:20px;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .wikipedia{font-size:15px;line-height:20px;text-align:left;margin-left:auto;margin-right:auto;margin-bottom:15px;clear:both;}.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .wikipedia .wiki-source{margin-bottom:15px;font-size:13px;line-height:19px;font-style:italic;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .wikipedia h4{border-bottom:1px solid #cccccc;margin-bottom:5px;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .wikipedia h4 a{color:#000000;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .wikipedia p{font-size:13px;line-height:19px;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .map{line-height:normal;z-index:200;text-align:left;background-color:#ffffff;}.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .map img{max-height:none !important;max-width:none !important;border:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .map .google-map{height:100%;width:100%;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .map .map-attribution{position:absolute;z-index:201;bottom:0px;width:100%;overflow:hidden;}.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .map .map-attribution .attribution-text{height:19px;overflow:hidden;-webkit-user-select:none;line-height:19px;margin-right:60px;padding-left:65px;font-family:Arial,sans-serif;font-size:10px;color:#444;white-space:nowrap;color:#ffffff;text-shadow:1px 1px 1px #333333;text-align:center;}.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .map .map-attribution .attribution-text a{color:#ffffff !important;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .credit{color:#999999;text-align:right;display:block;margin:0 auto;margin-top:6px;font-size:10px;line-height:13px;}
.vco-slider .slider-item .content .content-container .media .media-wrapper .media-container .caption{text-align:left;margin-top:10px;color:#666666;font-size:11px;line-height:14px;text-rendering:optimizeLegibility;word-wrap:break-word;}
.vco-slider .slider-item .content .content-container .media.text-media .media-wrapper .media-container{border:none;background-color:#ffffff;}
.vco-slider .slider-item .content .content-container .created-at{width:24px;height:24px;overflow:hidden;margin-left:7.5px;margin-top:2px;display:inline-block;float:right;filter:alpha(opacity=25);-khtml-opacity:0.25;-moz-opacity:0.25;opacity:0.25;}
.vco-slider .slider-item .content .content-container .storify .created-at{background-repeat:no-repeat;background-position:-328px -96px;}
.vco-slider .slider-item .content .content-container .twitter .created-at{background-repeat:no-repeat;background-position:-256px -24px;}
.vco-slider .slider-item .content .content-container .googleplus .googleplus-content{font-size:13px;line-height:19px;margin-bottom:6px;padding-top:10px;background-color:#ffffff;color:#666666;}.vco-slider .slider-item .content .content-container .googleplus .googleplus-content p{font-size:13px;line-height:19px;}
.vco-slider .slider-item .content .content-container .googleplus .googleplus-content .googleplus-title{font-size:24px;line-height:32px;margin-bottom:6px;padding-top:10px;background-color:#ffffff;color:#000000;}
.vco-slider .slider-item .content .content-container .googleplus .googleplus-content .googleplus-annotation{font-size:15px;line-height:20px;color:#000000;border-bottom:1px solid #e3e3e3;padding-bottom:7.5px;margin-bottom:7.5px;}
.vco-slider .slider-item .content .content-container .googleplus .googleplus-content .googleplus-attachments{border-top:1px solid #e3e3e3;padding-top:15px;margin-top:15px;border-bottom:1px solid #e3e3e3;padding-bottom:15px;margin-bottom:15px;*zoom:1;}.vco-slider .slider-item .content .content-container .googleplus .googleplus-content .googleplus-attachments:before,.vco-slider .slider-item .content .content-container .googleplus .googleplus-content .googleplus-attachments:after{display:table;content:"";}
.vco-slider .slider-item .content .content-container .googleplus .googleplus-content .googleplus-attachments:after{clear:both;}
.vco-slider .slider-item .content .content-container .googleplus .googleplus-content .googleplus-attachments h5{margin-bottom:5px;}
.vco-slider .slider-item .content .content-container .googleplus .googleplus-content .googleplus-attachments div{width:50%;padding-left:15px;display:inline-block;}
.vco-slider .slider-item .content .content-container .googleplus .googleplus-content .googleplus-attachments p{font-size:11px;line-height:14px;margin-bottom:5px;}
.vco-slider .slider-item .content .content-container .googleplus .googleplus-content .googleplus-attachments img{float:left;display:block;bottom:0;left:0;margin:auto;position:relative;right:0;top:0;width:40%;}
.vco-slider .slider-item .content .content-container .googleplus .proflinkPrefix{color:#0088cc;}
.vco-slider .slider-item .content .content-container .googleplus .created-at{background-repeat:no-repeat;background-position:-208px -72px;}
.vco-slider .slider-item .content .content-container .twitter,.vco-slider .slider-item .content .content-container .plain-text-quote,.vco-slider .slider-item .content .content-container .storify,.vco-slider .slider-item .content .content-container .googleplus{text-align:left;margin-left:auto;margin-right:auto;margin-bottom:15px;clear:both;}.vco-slider .slider-item .content .content-container .twitter blockquote,.vco-slider .slider-item .content .content-container .plain-text-quote blockquote,.vco-slider .slider-item .content .content-container .storify blockquote,.vco-slider .slider-item .content .content-container .googleplus blockquote{color:#666666;}.vco-slider .slider-item .content .content-container .twitter blockquote p,.vco-slider .slider-item .content .content-container .plain-text-quote blockquote p,.vco-slider .slider-item .content .content-container .storify blockquote p,.vco-slider .slider-item .content .content-container .googleplus blockquote p{font-size:24px;line-height:32px;margin-bottom:6px;padding-top:10px;background-color:#ffffff;color:#000000;}
.vco-slider .slider-item .content .content-container .twitter blockquote .quote-mark,.vco-slider .slider-item .content .content-container .plain-text-quote blockquote .quote-mark,.vco-slider .slider-item .content .content-container .storify blockquote .quote-mark,.vco-slider .slider-item .content .content-container .googleplus blockquote .quote-mark{color:#666666;}
.vco-slider .slider-item .content .content-container .twitter blockquote{font-size:15px;}.vco-slider .slider-item .content .content-container .twitter blockquote p{font-size:24px;}
.vco-slider .slider-item .content .content-container.layout-text-media .text-media{border-top:1px solid #e3e3e3;padding-top:15px;padding-right:0;}
.vco-slider .slider-item .content .content-container.layout-text-media.pad-left .text-media{padding-right:15px;padding-top:0;border-right:1px solid #e3e3e3;border-top:0px solid #e3e3e3;}
.vco-slider .slider-item .content .content-container.layout-text{width:100%;}.vco-slider .slider-item .content .content-container.layout-text .text{width:100%;max-width:100%;}.vco-slider .slider-item .content .content-container.layout-text .text .container{display:block;vertical-align:middle;padding:0px;width:90%;text-align:left;margin-left:auto;margin-right:auto;}
.vco-slider .slider-item .content .content-container.layout-media{width:100%;}.vco-slider .slider-item .content .content-container.layout-media .text{width:100%;height:100%;max-width:100%;display:block;text-align:center;}.vco-slider .slider-item .content .content-container.layout-media .text .container{display:block;text-align:center;width:100%;margin-left:none;margin-right:none;}
.vco-slider .slider-item .content .content-container.layout-media .media{width:100%;min-width:50%;float:none;}.vco-slider .slider-item .content .content-container.layout-media .media .media-wrapper .media-container{margin-left:auto;margin-right:auto;line-height:0px;padding:0px;}
.vco-slider .slider-item .content .content-container.layout-media .twitter,.vco-slider .slider-item .content .content-container.layout-media .wikipedia,.vco-slider .slider-item .content .content-container.layout-media .googleplus{max-width:70%;}
.storyjs-embed{background-color:#ffffff;margin-bottom:20px;border:1px solid #cccccc;padding-top:20px;padding-bottom:20px;clear:both;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;-webkit-box-shadow:1px 1px 3px rgba(0, 0, 0, 0.35);-moz-box-shadow:1px 1px 3px rgba(0, 0, 0, 0.35);box-shadow:1px 1px 3px rgba(0, 0, 0, 0.35);}
.storyjs-embed.full-embed{overflow:hidden;border:0 !important;padding:0 !important;margin:0 !important;clear:both;-webkit-border-radius:0 !important;-moz-border-radius:0 !important;border-radius:0 !important;-webkit-box-shadow:0 0px 0px rgba(0, 0, 0, 0.25) !important;-moz-box-shadow:0 0px 0px rgba(0, 0, 0, 0.25) !important;box-shadow:0 0px 0px rgba(0, 0, 0, 0.25) !important;}
.storyjs-embed.sized-embed{overflow:hidden;border:1px solid #cccccc;padding-top:7px;padding-bottom:7px;margin:0 !important;clear:both;-webkit-box-shadow:0 0px 0px rgba(0, 0, 0, 0.25) !important;-moz-box-shadow:0 0px 0px rgba(0, 0, 0, 0.25) !important;box-shadow:0 0px 0px rgba(0, 0, 0, 0.25) !important;}
.vco-storyjs{width:100%;height:100%;padding:0px;margin:0px;background-color:#ffffff;position:absolute;z-index:100;clear:both;overflow:hidden;}.vco-storyjs .vmm-clear:before,.vco-storyjs .vmm-clear:after{content:"";display:table;}
.vco-storyjs .vmm-clear:after{clear:both;}
.vco-storyjs .vmm-clear{*zoom:1;}
.vco-storyjs .vco-feature{width:100%;}.vco-storyjs .vco-feature .slider,.vco-storyjs .vco-feature .vco-slider{width:100%;float:left;position:relative;z-index:10;padding-top:15px;-webkit-box-shadow:1px 1px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:1px 1px 7px rgba(0, 0, 0, 0.3);box-shadow:1px 1px 7px rgba(0, 0, 0, 0.3);}
.vco-storyjs .vco-feedback{position:absolute;display:table;overflow:hidden;top:0px;left:0px;z-index:205;width:100%;height:100%;}
.vco-storyjs div.vco-loading,.vco-storyjs div.vco-explainer{display:table;text-align:center;min-width:100px;margin-top:15px;height:100%;width:100%;background-color:#ffffff;}.vco-storyjs div.vco-loading .vco-loading-container,.vco-storyjs div.vco-explainer .vco-loading-container,.vco-storyjs div.vco-loading .vco-explainer-container,.vco-storyjs div.vco-explainer .vco-explainer-container{display:table-cell;vertical-align:middle;}.vco-storyjs div.vco-loading .vco-loading-container .vco-loading-icon,.vco-storyjs div.vco-explainer .vco-loading-container .vco-loading-icon,.vco-storyjs div.vco-loading .vco-explainer-container .vco-loading-icon,.vco-storyjs div.vco-explainer .vco-explainer-container .vco-loading-icon{display:block;background-repeat:no-repeat;vertical-align:middle;margin-left:auto;margin-right:auto;text-align:center;background-image:url(loading.gif?v3.4);width:28px;height:28px;}
.vco-storyjs div.vco-loading .vco-loading-container .vco-gesture-icon,.vco-storyjs div.vco-explainer .vco-loading-container .vco-gesture-icon,.vco-storyjs div.vco-loading .vco-explainer-container .vco-gesture-icon,.vco-storyjs div.vco-explainer .vco-explainer-container .vco-gesture-icon{display:block;vertical-align:middle;margin-left:auto;margin-right:auto;text-align:center;background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-160px -160px;width:48px;height:48px;}
.vco-storyjs div.vco-loading .vco-loading-container .vco-message,.vco-storyjs div.vco-explainer .vco-loading-container .vco-message,.vco-storyjs div.vco-loading .vco-explainer-container .vco-message,.vco-storyjs div.vco-explainer .vco-explainer-container .vco-message{display:block;}
.vco-storyjs div.vco-loading .vco-loading-container .vco-message,.vco-storyjs div.vco-explainer .vco-loading-container .vco-message,.vco-storyjs div.vco-loading .vco-explainer-container .vco-message,.vco-storyjs div.vco-explainer .vco-explainer-container .vco-message,.vco-storyjs div.vco-loading .vco-loading-container .vco-message p,.vco-storyjs div.vco-explainer .vco-loading-container .vco-message p,.vco-storyjs div.vco-loading .vco-explainer-container .vco-message p,.vco-storyjs div.vco-explainer .vco-explainer-container .vco-message p{text-align:center;font-size:11px;line-height:13px;text-transform:uppercase;margin-top:7.5px;margin-bottom:7.5px;}
.vco-storyjs div.vco-explainer{background-color:transparent;}
.vco-storyjs .vco-bezel{background-color:#333333;background-color:rgba(0, 0, 0, 0.8);width:80px;height:50px;padding:50px;padding-top:25px;padding:25px 20px 50px 20px;margin:auto;-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;}.vco-storyjs .vco-bezel .vco-message,.vco-storyjs .vco-bezel .vco-message p{color:#ffffff;font-weight:bold;}
.vco-storyjs .vco-container.vco-main{position:absolute;top:0px;left:0px;padding-bottom:3px;width:auto;height:auto;margin:0px;clear:both;}
.vco-storyjs img,.vco-storyjs embed,.vco-storyjs object,.vco-storyjs video,.vco-storyjs iframe{max-width:100%;}
.vco-storyjs img{max-height:100%;border:1px solid #999999;}
.vco-storyjs a{color:#0088cc;text-decoration:none;}
.vco-storyjs a:hover{color:#005580;text-decoration:underline;}
.vco-storyjs .vcard{float:right;margin-bottom:15px;}.vco-storyjs .vcard a{color:#333333;}
.vco-storyjs .vcard a:hover{text-decoration:none;}.vco-storyjs .vcard a:hover .fn{text-decoration:underline;}
.vco-storyjs .vcard .fn,.vco-storyjs .vcard .nickname{padding-left:42px;}
.vco-storyjs .vcard .fn{display:block;font-weight:bold;}
.vco-storyjs .vcard .nickname{margin-top:1px;display:block;color:#666666;}
.vco-storyjs .vcard .avatar{float:left;display:block;width:32px;height:32px;}.vco-storyjs .vcard .avatar img{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;}
.vco-storyjs .thumbnail{width:24px;height:24px;overflow:hidden;float:left;margin:0;margin-right:1px;margin-top:6px;border:0;padding:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
.vco-storyjs a.thumbnail:hover{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}
.vco-storyjs .thumbnail.thumb-plaintext{background-repeat:no-repeat;background-position:-280px -48px;}
.vco-storyjs .thumbnail.thumb-quote{background-repeat:no-repeat;background-position:-232px -48px;}
.vco-storyjs .thumbnail.thumb-document{background-repeat:no-repeat;background-position:-256px -48px;}
.vco-storyjs .thumbnail.thumb-photo{background-repeat:no-repeat;background-position:-280px -24px;border:0;}.vco-storyjs .thumbnail.thumb-photo img{border:0px none #cccccc !important;}
.vco-storyjs .thumbnail.thumb-twitter{background-repeat:no-repeat;background-position:-256px -24px;}
.vco-storyjs .thumbnail.thumb-vimeo{background-repeat:no-repeat;background-position:-328px -48px;}
.vco-storyjs .thumbnail.thumb-vine{background-repeat:no-repeat;background-position:-232px -72px;}
.vco-storyjs .thumbnail.thumb-youtube{background-repeat:no-repeat;background-position:-328px -72px;}
.vco-storyjs .thumbnail.thumb-video{background-repeat:no-repeat;background-position:-328px -24px;}
.vco-storyjs .thumbnail.thumb-audio{background-repeat:no-repeat;background-position:-304px -24px;}
.vco-storyjs .thumbnail.thumb-map{background-repeat:no-repeat;background-position:-208px -48px;}
.vco-storyjs .thumbnail.thumb-website{background-repeat:no-repeat;background-position:-232px -24px;}
.vco-storyjs .thumbnail.thumb-link{background-repeat:no-repeat;background-position:-184px -72px;}
.vco-storyjs .thumbnail.thumb-wikipedia{background-repeat:no-repeat;background-position:-184px -48px;}
.vco-storyjs .thumbnail.thumb-storify{background-repeat:no-repeat;background-position:-328px -96px;}
.vco-storyjs .thumbnail.thumb-googleplus{background-repeat:no-repeat;background-position:-208px -72px;}
.vco-storyjs thumbnail.thumb-instagram{background-repeat:no-repeat;background-position:-208px -96px;}
.vco-storyjs thumbnail.thumb-instagram-full{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-232px -96px;width:48px;height:24px;}
.vco-storyjs .thumb-storify-full{height:12px;background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-280px -96px;width:48px;}
.vco-storyjs .thumbnail-inline{width:16px;height:14px;overflow:hidden;display:inline-block;margin-right:1px;margin-left:3px;margin-top:2px;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;}
.vco-storyjs .twitter .thumbnail-inline{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-160px -96px;}
.vco-storyjs .storify .thumbnail-inline{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-184px -96px;}
.vco-storyjs .googleplus .thumbnail-inline{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-208px -96px;}
.vco-storyjs .zFront{z-index:204;}
@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2){.vco-storyjs div.vco-loading .vco-loading-container .vco-loading-icon,.vco-storyjs div.vco-explainer .vco-loading-container .vco-loading-icon,.vco-storyjs div.vco-loading .vco-explainer-container .vco-loading-icon,.vco-storyjs div.vco-explainer .vco-explainer-container .vco-loading-icon{background-image:url(loading@2x.gif?v3.4);} .vco-storyjs div.vco-loading .vco-loading-container .vco-gesture-icon,.vco-storyjs div.vco-explainer .vco-loading-container .vco-gesture-icon,.vco-storyjs div.vco-loading .vco-explainer-container .vco-gesture-icon,.vco-storyjs div.vco-explainer .vco-explainer-container .vco-gesture-icon{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:-160px -160px;width:48px;height:48px;}}.vco-notouch .vco-navigation .vco-toolbar .zoom-in:hover,.vco-notouch .vco-navigation .vco-toolbar .zoom-out:hover,.vco-notouch .vco-navigation .vco-toolbar .back-home:hover{color:#0088cc;cursor:pointer;filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;}
.vco-notouch .vco-navigation .timenav .content .marker.active:hover{cursor:default;}.vco-notouch .vco-navigation .timenav .content .marker.active:hover .flag .flag-content h3,.vco-notouch .vco-navigation .timenav .content .marker.active:hover .flag-small .flag-content h3{color:#0088cc;}
.vco-notouch .vco-navigation .timenav .content .marker.active:hover .flag .flag-content h4,.vco-notouch .vco-navigation .timenav .content .marker.active:hover .flag-small .flag-content h4{color:#999999;}
.vco-notouch .vco-navigation .timenav .content .marker:hover .line{z-index:24;background:#999999;}
.vco-notouch .vco-navigation .timenav .content .marker .flag:hover,.vco-notouch .vco-navigation .timenav .content .marker .flag-small:hover{cursor:pointer;}.vco-notouch .vco-navigation .timenav .content .marker .flag:hover .flag-content h3,.vco-notouch .vco-navigation .timenav .content .marker .flag-small:hover .flag-content h3{color:#333333;}
.vco-notouch .vco-navigation .timenav .content .marker .flag:hover .flag-content h4,.vco-notouch .vco-navigation .timenav .content .marker .flag-small:hover .flag-content h4{color:#aaaaaa;}
.vco-notouch .vco-navigation .timenav .content .marker .flag:hover .flag-content .thumbnail,.vco-notouch .vco-navigation .timenav .content .marker .flag-small:hover .flag-content .thumbnail{filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;}
.vco-notouch .vco-navigation .timenav .content .marker .flag:hover{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:0 -53px;width:153px;height:53px;}
.vco-notouch .vco-navigation .timenav .content .marker .flag-small:hover{height:56px;background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:0 -53px;width:153px;height:53px;}.vco-notouch .vco-navigation .timenav .content .marker .flag-small:hover .flag-content{height:36px;}.vco-notouch .vco-navigation .timenav .content .marker .flag-small:hover .flag-content h3{margin-top:5px;}
.vco-notouch .vco-navigation .timenav .content .marker .flag-small.flag-small-last:hover{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:0 -109px;width:153px;height:26px;}.vco-notouch .vco-navigation .timenav .content .marker .flag-small.flag-small-last:hover .flag-content{height:14px;}.vco-notouch .vco-navigation .timenav .content .marker .flag-small.flag-small-last:hover .flag-content h3{margin-top:4px;}
.vco-timeline .vco-navigation{clear:both;cursor:move;width:100%;height:200px;border-top:1px solid #e3e3e3;position:relative;}.vco-timeline .vco-navigation .vco-toolbar{position:absolute;top:45px;left:0px;z-index:202;background-color:#ffffff;border:1px solid #cccccc;-webkit-box-shadow:1px 1px 0px rgba(0, 0, 0, 0.2);-moz-box-shadow:1px 1px 0px rgba(0, 0, 0, 0.2);box-shadow:1px 1px 0px rgba(0, 0, 0, 0.2);}.vco-timeline .vco-navigation .vco-toolbar .zoom-in,.vco-timeline .vco-navigation .vco-toolbar .zoom-out,.vco-timeline .vco-navigation .vco-toolbar .back-home{font-weight:normal;font-size:10px;line-height:20px;top:0px;z-index:202;width:18px;height:18px;color:#333333;text-align:center;font-weight:bold;border:1px solid #ffffff;padding:5px;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;}
.vco-timeline .vco-navigation .vco-toolbar .zoom-in .icon{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-256px 0;width:24px;height:24px;}
.vco-timeline .vco-navigation .vco-toolbar .zoom-out .icon{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-280px 0;width:24px;height:24px;}
.vco-timeline .vco-navigation .vco-toolbar .back-home .icon{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-328px 0;width:24px;height:24px;}
.vco-timeline .vco-navigation .vco-toolbar.touch{-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;background-color:transparent;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}.vco-timeline .vco-navigation .vco-toolbar.touch .zoom-in,.vco-timeline .vco-navigation .vco-toolbar.touch .zoom-out,.vco-timeline .vco-navigation .vco-toolbar.touch .back-home{width:40px;height:40px;padding:5px;background-color:#ffffff;border:1px solid #cccccc;-webkit-box-shadow:1px 1px 0px rgba(0, 0, 0, 0.2);-moz-box-shadow:1px 1px 0px rgba(0, 0, 0, 0.2);box-shadow:1px 1px 0px rgba(0, 0, 0, 0.2);-webkit-border-radius:10px;-moz-border-radius:10px;border-radius:10px;filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;}
.vco-timeline .vco-navigation .vco-toolbar.touch .zoom-in .icon{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-208px -160px;width:40px;height:40px;}
.vco-timeline .vco-navigation .vco-toolbar.touch .zoom-out .icon{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-256px -160px;width:40px;height:40px;}
.vco-timeline .vco-navigation .vco-toolbar.touch .back-home .icon{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-304px -160px;width:40px;height:40px;}
.vco-timeline .vco-navigation .timenav-background{position:absolute;cursor:move;top:0px;left:0px;height:150px;width:100%;background-color:#e9e9e9;}.vco-timeline .vco-navigation .timenav-background .timenav-interval-background{position:absolute;top:151px;left:0px;background:#ffffff;width:100%;height:49px;-webkit-box-shadow:-1px -1px 7px rgba(0, 0, 0, 0.1);-moz-box-shadow:-1px -1px 7px rgba(0, 0, 0, 0.1);box-shadow:-1px -1px 7px rgba(0, 0, 0, 0.1);}.vco-timeline .vco-navigation .timenav-background .timenav-interval-background .top-highlight{position:absolute;top:-1px;left:0px;z-index:30;width:100%;height:1px;background:#ffffff;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;-webkit-box-shadow:1px 1px 5px rgba(0, 0, 0, 0.2);-moz-box-shadow:1px 1px 5px rgba(0, 0, 0, 0.2);box-shadow:1px 1px 5px rgba(0, 0, 0, 0.2);}
.vco-timeline .vco-navigation .timenav-background .timenav-line{position:absolute;top:0px;left:50%;width:3px;height:150px;background-color:#0088cc;z-index:1;-webkit-box-shadow:1px 1px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:1px 1px 7px rgba(0, 0, 0, 0.3);box-shadow:1px 1px 7px rgba(0, 0, 0, 0.3);}
.vco-timeline .vco-navigation .timenav-background .timenav-indicator{position:absolute;top:-1px;left:50%;z-index:202;background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-160px -48px;width:24px;height:24px;}
.vco-timeline .vco-navigation .timenav-background .timenav-tag div{height:50px;display:table;}.vco-timeline .vco-navigation .timenav-background .timenav-tag div h3{display:table-cell;vertical-align:middle;padding-left:65px;font-size:15px;color:#d0d0d0;font-weight:bold;text-shadow:0px 1px 1px #ffffff;}
.vco-timeline .vco-navigation .timenav-background .timenav-tag-size-half{height:25px;}.vco-timeline .vco-navigation .timenav-background .timenav-tag-size-half div{height:25px;}
.vco-timeline .vco-navigation .timenav-background .timenav-tag-size-full{height:50px;}.vco-timeline .vco-navigation .timenav-background .timenav-tag-size-full div{height:50px;}
.vco-timeline .vco-navigation .timenav-background .timenav-tag-row-2,.vco-timeline .vco-navigation .timenav-background .timenav-tag-row-4,.vco-timeline .vco-navigation .timenav-background .timenav-tag-row-6{background:#f1f1f1;}
.vco-timeline .vco-navigation .timenav-background .timenav-tag-row-1,.vco-timeline .vco-navigation .timenav-background .timenav-tag-row-3,.vco-timeline .vco-navigation .timenav-background .timenav-tag-row-5{background:#e9e9e9;}
.vco-timeline .vco-navigation .timenav{position:absolute;top:0px;left:-250px;z-index:1;}.vco-timeline .vco-navigation .timenav .content{position:relative;}.vco-timeline .vco-navigation .timenav .content .marker.start{display:none;}
.vco-timeline .vco-navigation .timenav .content .marker.active .dot{background:#0088cc;z-index:200;}
.vco-timeline .vco-navigation .timenav .content .marker.active .line{z-index:199;background:#0088cc;width:1px;}.vco-timeline .vco-navigation .timenav .content .marker.active .line .event-line{background:#0088cc;filter:alpha(opacity=75);-khtml-opacity:0.75;-moz-opacity:0.75;opacity:0.75;}
.vco-timeline .vco-navigation .timenav .content .marker.active .flag,.vco-timeline .vco-navigation .timenav .content .marker.active .flag-small{z-index:200;}.vco-timeline .vco-navigation .timenav .content .marker.active .flag .flag-content,.vco-timeline .vco-navigation .timenav .content .marker.active .flag-small .flag-content{height:36px;}.vco-timeline .vco-navigation .timenav .content .marker.active .flag .flag-content h3,.vco-timeline .vco-navigation .timenav .content .marker.active .flag-small .flag-content h3{color:#0088cc;margin-top:5px;}
.vco-timeline .vco-navigation .timenav .content .marker.active .flag .flag-content .thumbnail,.vco-timeline .vco-navigation .timenav .content .marker.active .flag-small .flag-content .thumbnail{filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;}
.vco-timeline .vco-navigation .timenav .content .marker.active .flag.row1,.vco-timeline .vco-navigation .timenav .content .marker.active .flag.row2,.vco-timeline .vco-navigation .timenav .content .marker.active .flag.row3,.vco-timeline .vco-navigation .timenav .content .marker.active .flag-small.row1,.vco-timeline .vco-navigation .timenav .content .marker.active .flag-small.row2,.vco-timeline .vco-navigation .timenav .content .marker.active .flag-small.row3{z-index:200;}
.vco-timeline .vco-navigation .timenav .content .marker.active .flag{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:0 -53px;width:153px;height:53px;}
.vco-timeline .vco-navigation .timenav .content .marker.active .flag-small{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:0 -109px;width:153px;height:26px;}.vco-timeline .vco-navigation .timenav .content .marker.active .flag-small .flag-content{height:14px;}.vco-timeline .vco-navigation .timenav .content .marker.active .flag-small .flag-content h3{margin-top:4px;}
.vco-timeline .vco-navigation .timenav .content .marker{position:absolute;top:0px;left:150px;display:block;}.vco-timeline .vco-navigation .timenav .content .marker .dot{position:absolute;top:150px;left:0px;display:block;width:6px;height:6px;background:#333333;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;z-index:21;}
.vco-timeline .vco-navigation .timenav .content .marker .line{position:absolute;top:0px;left:3px;width:1px;height:150px;background-color:#cccccc;background-color:rgba(204, 204, 204, 0.5);-webkit-box-shadow:1px 0 0 rgba(255, 255, 255, 0.5);-moz-box-shadow:1px 0 0 rgba(255, 255, 255, 0.5);box-shadow:1px 0 0 rgba(255, 255, 255, 0.5);z-index:22;}.vco-timeline .vco-navigation .timenav .content .marker .line .event-line{position:absolute;z-index:22;left:0px;height:1px;width:1px;background:#0088cc;filter:alpha(opacity=15);-khtml-opacity:0.15;-moz-opacity:0.15;opacity:0.15;}
.vco-timeline .vco-navigation .timenav .content .marker .flag,.vco-timeline .vco-navigation .timenav .content .marker .flag-small{position:absolute;top:15px;left:3px;padding:0px;display:block;z-index:23;width:153px;}.vco-timeline .vco-navigation .timenav .content .marker .flag .flag-content,.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content{padding:0px 7px 2px 6px;overflow:hidden;}.vco-timeline .vco-navigation .timenav .content .marker .flag .flag-content h3,.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content h3{font-weight:bold;font-size:15px;line-height:20px;font-size:11px;line-height:11px;color:#999999;margin-bottom:2px;}.vco-timeline .vco-navigation .timenav .content .marker .flag .flag-content h3 small,.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content h3 small{display:none;}
.vco-timeline .vco-navigation .timenav .content .marker .flag .flag-content h4,.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content h4{display:none;font-weight:normal;font-size:15px;line-height:20px;margin-top:5px;font-size:10px;line-height:10px;color:#aaaaaa;}.vco-timeline .vco-navigation .timenav .content .marker .flag .flag-content h4 small,.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content h4 small{display:none;}
.vco-timeline .vco-navigation .timenav .content .marker .flag .flag-content .thumbnail,.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail{margin-bottom:15px;margin-right:3px;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;}.vco-timeline .vco-navigation .timenav .content .marker .flag .flag-content .thumbnail img,.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail img{width:22px;height:22px;max-height:none;max-width:none;border:0;border:1px solid #999999;padding:0;margin:0;}
.vco-timeline .vco-navigation .timenav .content .marker .flag{height:56px;background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:0 0;width:153px;height:53px;}.vco-timeline .vco-navigation .timenav .content .marker .flag .flag-content{height:36px;}.vco-timeline .vco-navigation .timenav .content .marker .flag .flag-content h3{margin-top:5px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:0 -135px;width:153px;height:26px;}.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content{height:14px;}.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content h3{margin-top:4px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail{width:16px;height:10px;margin-right:1px;margin-top:6px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-plaintext{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-280px -130px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-quote{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-232px -130px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-document{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-256px -130px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-photo{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-280px -120px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-twitter{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-256px -120px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-vimeo{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-328px -130px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-vine{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-160px -120px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-youtube{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-304px -130px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-video{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-328px -120px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-audio{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-304px -120px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-map{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-208px -120px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-website{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-232px -120px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-link{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-232px -120px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-wikipedia{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-184px -120px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-storify{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-184px -130px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content .thumbnail.thumb-googleplus{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-208px -130px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small .flag-content thumbnail.thumb-instagram{background-image:url(timeline.png?v4.4);background-repeat:no-repeat;background-position:-208px -96px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag.row1{z-index:25;top:48px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag.row2{z-index:24;top:96px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag.row3{z-index:23;top:1px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small.row1{z-index:28;top:24px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small.row2{z-index:27;top:48px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small.row3{z-index:26;top:72px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small.row4{z-index:25;top:96px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small.row5{z-index:24;top:120px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag-small.row6{z-index:23;top:1px;}
.vco-timeline .vco-navigation .timenav .content .marker .flag.zFront,.vco-timeline .vco-navigation .timenav .content .marker .flag-small.zFront{z-index:201;}
.vco-timeline .vco-navigation .timenav .content .era{position:absolute;top:138px;left:150px;height:12px;display:block;overflow:hidden;}.vco-timeline .vco-navigation .timenav .content .era div{height:50px;width:100%;height:100%;line-height:0px;background:#e9e9e9;background:rgba(233, 233, 233, 0.33);}.vco-timeline .vco-navigation .timenav .content .era div h3,.vco-timeline .vco-navigation .timenav .content .era div h4{position:absolute;bottom:1px;padding-left:15px;font-size:15px;font-weight:bold;color:rgba(0, 136, 204, 0.35);text-shadow:0px 1px 1px #ffffff;}
.vco-timeline .vco-navigation .timenav .content .era1 div{background:#cc4400;filter:alpha(opacity=10);-khtml-opacity:0.1;-moz-opacity:0.1;opacity:0.1;border-left:1px solid rgba(204, 68, 0, 0.1);border-right:1px solid rgba(255, 85, 0, 0.05);}.vco-timeline .vco-navigation .timenav .content .era1 div h3,.vco-timeline .vco-navigation .timenav .content .era1 div h4{color:rgba(204, 68, 0, 0.35);text-shadow:0px 1px 1px #ffffff;}
.vco-timeline .vco-navigation .timenav .content .era2 div{background:#cc0022;filter:alpha(opacity=10);-khtml-opacity:0.1;-moz-opacity:0.1;opacity:0.1;border-left:1px solid rgba(204, 0, 34, 0.1);border-right:1px solid rgba(255, 0, 43, 0.05);}.vco-timeline .vco-navigation .timenav .content .era2 div h3,.vco-timeline .vco-navigation .timenav .content .era2 div h4{color:rgba(204, 0, 34, 0.35);text-shadow:0px 1px 1px #ffffff;}
.vco-timeline .vco-navigation .timenav .content .era3 div{background:#0022cc;filter:alpha(opacity=10);-khtml-opacity:0.1;-moz-opacity:0.1;opacity:0.1;border-left:1px solid rgba(0, 34, 204, 0.1);border-right:1px solid rgba(0, 43, 255, 0.05);}.vco-timeline .vco-navigation .timenav .content .era3 div h3,.vco-timeline .vco-navigation .timenav .content .era3 div h4{color:rgba(0, 34, 204, 0.35);text-shadow:0px 1px 1px #ffffff;}
.vco-timeline .vco-navigation .timenav .content .era4 div{background:#ccaa00;filter:alpha(opacity=10);-khtml-opacity:0.1;-moz-opacity:0.1;opacity:0.1;border-left:1px solid rgba(204, 170, 0, 0.1);border-right:1px solid rgba(255, 213, 0, 0.05);}.vco-timeline .vco-navigation .timenav .content .era4 div h3,.vco-timeline .vco-navigation .timenav .content .era4 div h4{color:rgba(204, 170, 0, 0.35);text-shadow:0px 1px 1px #ffffff;}
.vco-timeline .vco-navigation .timenav .content .era5 div{background:#00ccaa;filter:alpha(opacity=10);-khtml-opacity:0.1;-moz-opacity:0.1;opacity:0.1;border-left:1px solid rgba(0, 204, 170, 0.1);border-right:1px solid rgba(0, 255, 213, 0.05);}.vco-timeline .vco-navigation .timenav .content .era5 div h3,.vco-timeline .vco-navigation .timenav .content .era5 div h4{color:rgba(0, 204, 170, 0.35);text-shadow:0px 1px 1px #ffffff;}
.vco-timeline .vco-navigation .timenav .content .era6 div{background:#0088cc;filter:alpha(opacity=10);-khtml-opacity:0.1;-moz-opacity:0.1;opacity:0.1;border-left:1px solid rgba(0, 136, 204, 0.1);border-right:1px solid rgba(0, 170, 255, 0.05);}.vco-timeline .vco-navigation .timenav .content .era6 div h3,.vco-timeline .vco-navigation .timenav .content .era6 div h4{color:rgba(0, 136, 204, 0.35);text-shadow:0px 1px 1px #ffffff;}
.vco-timeline .vco-navigation .timenav .time{position:absolute;left:0px;top:150px;height:50px;background-color:#ffffff;line-height:0px;}.vco-timeline .vco-navigation .timenav .time .time-interval-minor{max-width:none;height:6px;white-space:nowrap;position:absolute;top:-2px;left:8px;z-index:10;}.vco-timeline .vco-navigation .timenav .time .time-interval-minor .minor{position:relative;top:2px;display:inline-block;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAMCAMAAACdvocfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRFzMzM////040VdgAAAAJ0Uk5T/wDltzBKAAAAEklEQVR42mJgYAQCBopJgAADAAbwADHy2qHzAAAAAElFTkSuQmCC);width:100px;height:6px;background-position:center top;white-space:nowrap;color:#666666;margin-top:0px;padding-top:0px;}
.vco-timeline .vco-navigation .timenav .time .time-interval{white-space:nowrap;position:absolute;top:5px;left:0px;}.vco-timeline .vco-navigation .timenav .time .time-interval div{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAMCAMAAACdvocfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRFzMzM////040VdgAAAAJ0Uk5T/wDltzBKAAAAEklEQVR42mJgYAQCBopJgAADAAbwADHy2qHzAAAAAElFTkSuQmCC);background-position:left top;background-repeat:no-repeat;padding-top:6px;position:absolute;height:3px;left:0px;display:block;font-weight:normal;font-size:10px;line-height:20px;text-transform:uppercase;text-align:left;text-indent:0px;white-space:nowrap;color:#666666;margin-left:0px;margin-right:0px;margin-top:0px;z-index:2;}.vco-timeline .vco-navigation .timenav .time .time-interval div strong{font-weight:bold;color:#000000;}
.vco-timeline .vco-navigation .timenav .time .time-interval div.era{font-weight:bold;padding-top:0px;margin-top:-3px;margin-left:2px;background-image:none;}
.vco-timeline .vco-navigation .timenav .time .time-interval .era1{color:#cc4400;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;}
.vco-timeline .vco-navigation .timenav .time .time-interval .era2{color:#cc0022;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;}
.vco-timeline .vco-navigation .timenav .time .time-interval .era3{color:#0022cc;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;}
.vco-timeline .vco-navigation .timenav .time .time-interval .era4{color:#ccaa00;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;}
.vco-timeline .vco-navigation .timenav .time .time-interval .era5{color:#00ccaa;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;}
.vco-timeline .vco-navigation .timenav .time .time-interval .era6{color:#0088cc;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;}
.vco-timeline .vco-navigation .timenav .time .time-interval-major{white-space:nowrap;position:absolute;top:5px;left:0px;}.vco-timeline .vco-navigation .timenav .time .time-interval-major div{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAQAQMAAADtUYf0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyOTAzRjI3REIzNDcxMUUxQUQ3QUZCOThEODQ1NDhCNyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoyOTAzRjI3RUIzNDcxMUUxQUQ3QUZCOThEODQ1NDhCNyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjI5MDNGMjdCQjM0NzExRTFBRDdBRkI5OEQ4NDU0OEI3IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjI5MDNGMjdDQjM0NzExRTFBRDdBRkI5OEQ4NDU0OEI3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+DPWNfQAAAANQTFRFzMzMylJEJwAAAAtJREFUCB1jYMAPAAAgAAHDvpOtAAAAAElFTkSuQmCC);background-position:left top;background-repeat:no-repeat;padding-top:15px;position:absolute;height:15px;left:0px;display:block;font-weight:bold;font-size:12px;line-height:20px;text-transform:uppercase;text-align:left;text-indent:0px;white-space:nowrap;color:#333333;margin-left:0px;margin-right:0px;margin-top:1px;z-index:5;}.vco-timeline .vco-navigation .timenav .time .time-interval-major div strong{font-weight:bold;color:#000000;}
@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2){.vco-notouch .vco-navigation .vco-toolbar .zoom-in .icon{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:-256px 0;width:24px;height:24px;} .vco-notouch .vco-navigation .vco-toolbar .zoom-out .icon{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:-280px 0;width:24px;height:24px;} .vco-notouch .vco-navigation .vco-toolbar .back-home .icon{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:-328px 0;width:24px;height:24px;} .vco-notouch .vco-navigation .vco-toolbar.touch .zoom-in .icon{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:-208px -160px;width:40px;height:40px;} .vco-notouch .vco-navigation .vco-toolbar.touch .zoom-out .icon{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:-256px -160px;width:40px;height:40px;} .vco-notouch .vco-navigation .vco-toolbar.touch .back-home .icon{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:-304px -160px;width:40px;height:40px;} .vco-notouch .vco-navigation .timenav .content .marker .flag:hover{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:0 -53px;width:153px;height:53px;} .vco-notouch .vco-navigation .timenav .content .marker .flag-small:hover{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:0 -53px;width:153px;height:53px;} .vco-notouch .vco-navigation .timenav .content .marker .flag-small.flag-small-last:hover{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:0 -109px;width:153px;height:26px;} .vco-notouch .vco-navigation .timenav-background .timenav-indicator{background-image:url(timeline@2x.png?v4.4);background-size:352px 260px;background-repeat:no-repeat;background-position:-160px -48px;width:24px;height:24px;}}@media screen and (-webkit-max-device-pixel-ratio:1){}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (-o-min-device-pixel-ratio:3/2),only screen and (min--moz-device-pixel-ratio:1.5),only screen and (min-device-pixel-ratio:1.5){}@media screen and (max-device-width:480px) and (orientation:portrait){.storyjs-embed.full-embed{height:557px !important;width:320px !important;}.storyjs-embed.full-embed .vco-feature{height:356px !important;}}@media screen and (max-device-width:480px) and (orientation:landscape){.storyjs-embed.full-embed{height:409px !important;width:480px !important;}.storyjs-embed.full-embed .vco-feature{height:208px !important;}}@media screen and (min-device-width:481px) and (orientation:portrait){}@media screen and (min-device-width:481px) and (orientation:landscape){}@media (max-width:480px){}@media only screen and (max-width:480px){}

BIN
vendor/timeline/css/timeline.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
vendor/timeline/css/timeline@2x.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

10015
vendor/timeline/js/timeline.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,14 @@
// Underscore.js 1.4.2
// http://underscorejs.org
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore may be freely distributed under the MIT license.
// Underscore.js 1.4.4
// ===================
// > http://underscorejs.org
// > (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
// > Underscore may be freely distributed under the MIT license.
// Baseline setup
// --------------
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this;
@ -24,7 +25,6 @@
var push = ArrayProto.push,
slice = ArrayProto.slice,
concat = ArrayProto.concat,
unshift = ArrayProto.unshift,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
@ -61,11 +61,11 @@
}
exports._ = _;
} else {
root['_'] = _;
root._ = _;
}
// Current version.
_.VERSION = '1.4.2';
_.VERSION = '1.4.4';
// Collection Functions
// --------------------
@ -102,6 +102,8 @@
return results;
};
var reduceError = 'Reduce of empty array with no initial value';
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
@ -119,7 +121,7 @@
memo = iterator.call(context, memo, value, index, list);
}
});
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
if (!initial) throw new TypeError(reduceError);
return memo;
};
@ -130,7 +132,7 @@
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return arguments.length > 2 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var length = obj.length;
if (length !== +length) {
@ -146,7 +148,7 @@
memo = iterator.call(context, memo, obj[index], index, list);
}
});
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
if (!initial) throw new TypeError(reduceError);
return memo;
};
@ -177,12 +179,9 @@
// Return all the elements for which a truth test fails.
_.reject = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
each(obj, function(value, index, list) {
if (!iterator.call(context, value, index, list)) results[results.length] = value;
});
return results;
return _.filter(obj, function(value, index, list) {
return !iterator.call(context, value, index, list);
}, context);
};
// Determine whether all of the elements match a truth test.
@ -216,20 +215,19 @@
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
var found = false;
if (obj == null) return found;
if (obj == null) return false;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
found = any(obj, function(value) {
return any(obj, function(value) {
return value === target;
});
return found;
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
var isFunc = _.isFunction(method);
return _.map(obj, function(value) {
return (_.isFunction(method) ? method : value[method]).apply(value, args);
return (isFunc ? method : value[method]).apply(value, args);
});
};
@ -239,10 +237,10 @@
};
// Convenience version of a common use case of `filter`: selecting only objects
// with specific `key:value` pairs.
_.where = function(obj, attrs) {
if (_.isEmpty(attrs)) return [];
return _.filter(obj, function(value) {
// containing specific `key:value` pairs.
_.where = function(obj, attrs, first) {
if (_.isEmpty(attrs)) return first ? null : [];
return _[first ? 'find' : 'filter'](obj, function(value) {
for (var key in attrs) {
if (attrs[key] !== value[key]) return false;
}
@ -250,6 +248,12 @@
});
};
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_.findWhere = function(obj, attrs) {
return _.where(obj, attrs, true);
};
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See: https://bugs.webkit.org/show_bug.cgi?id=80797
@ -258,7 +262,7 @@
return Math.max.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return -Infinity;
var result = {computed : -Infinity};
var result = {computed : -Infinity, value: -Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed >= result.computed && (result = {value : value, computed : computed});
@ -272,7 +276,7 @@
return Math.min.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return Infinity;
var result = {computed : Infinity};
var result = {computed : Infinity, value: Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed < result.computed && (result = {value : value, computed : computed});
@ -321,7 +325,7 @@
// An internal function used for aggregate "group by" operations.
var group = function(obj, value, context, behavior) {
var result = {};
var iterator = lookupIterator(value);
var iterator = lookupIterator(value || _.identity);
each(obj, function(value, index) {
var key = iterator.call(context, value, index, obj);
behavior(result, key, value);
@ -341,7 +345,7 @@
// either a string attribute to count by, or a function that returns the
// criterion.
_.countBy = function(obj, value, context) {
return group(obj, value, context, function(result, key, value) {
return group(obj, value, context, function(result, key) {
if (!_.has(result, key)) result[key] = 0;
result[key]++;
});
@ -363,12 +367,14 @@
// Safely convert anything iterable into a real, live array.
_.toArray = function(obj) {
if (!obj) return [];
if (obj.length === +obj.length) return slice.call(obj);
if (_.isArray(obj)) return slice.call(obj);
if (obj.length === +obj.length) return _.map(obj, _.identity);
return _.values(obj);
};
// Return the number of elements in an object.
_.size = function(obj) {
if (obj == null) return 0;
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
};
@ -379,6 +385,7 @@
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0;
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
};
@ -393,6 +400,7 @@
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) {
if (array == null) return void 0;
if ((n != null) && !guard) {
return slice.call(array, Math.max(array.length - n, 0));
} else {
@ -410,7 +418,7 @@
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.filter(array, function(value){ return !!value; });
return _.filter(array, _.identity);
};
// Internal implementation of a recursive `flatten` function.
@ -439,6 +447,11 @@
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator, context) {
if (_.isFunction(isSorted)) {
context = iterator;
iterator = isSorted;
isSorted = false;
}
var initial = iterator ? _.map(array, iterator, context) : array;
var results = [];
var seen = [];
@ -491,6 +504,7 @@
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
_.object = function(list, values) {
if (list == null) return {};
var result = {};
for (var i = 0, l = list.length; i < l; i++) {
if (values) {
@ -561,25 +575,23 @@
// Function (ahem) Functions
// ------------------
// Reusable constructor function for prototype setting.
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Binding with arguments is also known as `curry`.
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
// We check for `func.bind` first, to fail fast when `func` is undefined.
_.bind = function bind(func, context) {
var bound, args;
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
_.bind = function(func, context) {
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
var args = slice.call(arguments, 2);
return function() {
return func.apply(context, args.concat(slice.call(arguments)));
};
};
// Partially apply a function by creating a version that has had some of its
// arguments pre-filled, without changing its dynamic `this` context.
_.partial = function(func) {
var args = slice.call(arguments, 1);
return function() {
return func.apply(this, args.concat(slice.call(arguments)));
};
};
@ -587,7 +599,7 @@
// all callbacks defined on an object belong to it.
_.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
if (funcs.length == 0) funcs = _.functions(obj);
if (funcs.length === 0) funcs = _.functions(obj);
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
@ -618,25 +630,26 @@
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
_.throttle = function(func, wait) {
var context, args, timeout, throttling, more, result;
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
var context, args, timeout, result;
var previous = 0;
var later = function() {
previous = new Date;
timeout = null;
result = func.apply(context, args);
};
return function() {
context = this; args = arguments;
var later = function() {
var now = new Date;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
if (more) {
result = func.apply(context, args);
}
whenDone();
};
if (!timeout) timeout = setTimeout(later, wait);
if (throttling) {
more = true;
} else {
throttling = true;
previous = now;
result = func.apply(context, args);
} else if (!timeout) {
timeout = setTimeout(later, remaining);
}
whenDone();
return result;
};
};
@ -754,8 +767,10 @@
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
obj[prop] = source[prop];
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
@ -784,8 +799,10 @@
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
if (obj[prop] == null) obj[prop] = source[prop];
if (source) {
for (var prop in source) {
if (obj[prop] == null) obj[prop] = source[prop];
}
}
});
return obj;
@ -950,7 +967,7 @@
// Is a given object a finite number?
_.isFinite = function(obj) {
return _.isNumber(obj) && isFinite(obj);
return isFinite(obj) && !isNaN(parseFloat(obj));
};
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
@ -996,7 +1013,9 @@
// Run a function **n** times.
_.times = function(n, iterator, context) {
for (var i = 0; i < n; i++) iterator.call(context, i);
var accum = Array(n);
for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
return accum;
};
// Return a random integer between min and max (inclusive).
@ -1005,7 +1024,7 @@
max = min;
min = 0;
}
return min + (0 | Math.random() * (max - min + 1));
return min + Math.floor(Math.random() * (max - min + 1));
};
// List of HTML entities for escaping.
@ -1061,7 +1080,7 @@
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = idCounter++;
var id = ++idCounter + '';
return prefix ? prefix + id : id;
};
@ -1096,6 +1115,7 @@
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
@ -1111,11 +1131,18 @@
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
source +=
escape ? "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'" :
interpolate ? "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'" :
evaluate ? "';\n" + evaluate + "\n__p+='" : '';
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
return match;
});
source += "';\n";
@ -1127,7 +1154,7 @@
source + "return __p;\n";
try {
var render = new Function(settings.variable || 'obj', '_', source);
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;