EVEoj - 0.3.1

EVE Online JavaScript library

The EVEoj project is designed to push as much of the EVE logic and data into the browser as feasible in order to simplify the development of web tools for EVE. Code that doesn't NEED to run on a back-end server shouldn't HAVE to. It should be possible to write many tools with just HTML, JavaScript, and static hosting.

info
 
 

EVEoj tries to turn that possibility into reality.

For discussion and feedback, please use this EVE Online forum thread.
For issues and bugs, please refer to the GitHub issue tracker.

download bling

Downloads

EVEoj requires jQuery.

Node.js support added in 0.2.x! Node.js functionality does not depend on jQuery, but JSON sources must be on the local file system.

EVEoj
0.3.1 notes download CDN: https://cf.eve-oj.com/js/EVEoj-0.3.x.min.js
Static Data

The most recent version of the static data on the CDN is always available at:
https://cf.eve-oj.com/sdd/latest.

201611140 (Ascension 1.0) JSON (37 MB) CDN: https://cf.eve-oj.com/sdd/201611140
201608090 (YC 118.7) JSON (37 MB) CDN: https://cf.eve-oj.com/sdd/201608090
201607040 (YC 118.6) JSON (37 MB) CDN: https://cf.eve-oj.com/sdd/201607040

Getting Started

We all love Jita, but sometimes we'd like to visit (and shop!) in Amarr. Let's find out exactly how many jumps that trip will take with EVEoj in Node.js.

Getting Started - Jita to Amarr route length in Node.js

Coming at this as a Node.js developer instead of writing a web tool? This example
will display the route length from Jita to Amarr in Node.js.

Add the EVEoj package to your dependency list and download a current version of the JSON static data. Place the static data somewhere on a file-system accessible to your Node.js project.

var EVEoj = require("EVEoj"),
    SDD = EVEoj.SDD.Create("json", {
        path: "D:/static/sdd/201604290"
    }),
    map;

SDD.LoadMeta()
.then(function() {
    map = EVEoj.map.Create(SDD, "K");
    return map.Load();
})
.then(function() {
    var jita = map.GetSystem({name: "Jita"});
    var amarr = map.GetSystem({name: "Amarr"});
    var route = map.Route(jita.ID, amarr.ID, [], false, false);
    console.log("Jita to Amarr route length: " + route.length);
})
.caught(function(err) {
    console.error(err);
});

Now let's get the item ID of something; perhaps so we can fetch some market data while we're busy shopping in Amarr? Sure, there are tools out there that can give us item ID's, but to build this functionality into OUR tool, we'd have to implement it from scratch. Let's try it out with EVEoj in the web browser.

Getting Started - item ID lookup in the web browser

This is the only time we'll talk to the server, and we're just grabbing static files, so any old web-server will do.

The first thing we need is a static data source. We create one with the 'json' source type and provide config settings to use the CDN hosted JSON (CORS-enabled) data.

var SDD = EVEoj.SDD.Create('json', {'path': 'https://cf.eve-oj.com/sdd/' + ver});

Once we have the SDD object, we need to load its metadata. This fetches information about the available tables and data. Since this is an asynchronous operation, it returns a chainable Promise object that is used to add success and failure handlers.

SDD.LoadMeta()
    .then(function(arg){
        return arg.source.GetTable('invTypes').Load();
    })
    .then(my_load_done_handler);

We use an anonymous success handler on the LoadMeta call to immediately turn around and get the "invTypes" table. This table is ~300KB of gzipped JSON data and should load quickly.

The table must be loaded asynchronously so it also uses Promises. In this case, we return the updated Promise chain, then add a custom handler called "my_load_done_handler" to be called when the table loading is successful.

function my_load_done_handler(arg) {
    var name = $('#field_with_my_name').val(),
        rxp = new RegExp('^' + name, 'i'),
        tbl = arg.table,
        results;

    results = _.filter(tbl.data, function(entry) {
        if (rxp.test(entry[tbl.c.typeName])) return true;
        return false;
    });
    _.each(results, function(val, key, list) {
        var typeName = val[tbl.c.typeName],
            typeID = val[tbl.c.typeID];
	
        // do something with typeName and typeID
    });
}

Our table loading handler gets the name to search for from "somewhere". It then uses Underscore.js to filter the table data based on a regular expression test. We reference each entry's "typeName" data using the column mapping provided by the table (tbl.c). Finally, we use underscore again to iterate through our result set and do something with each matching result that was found.

While underscore is not a required dependency to use EVEoj, it does happen to be an extremely useful way to work with the data sets and interacting cleanly with underscore is a goal of the EVEoj core data model.

CONCORD
 
 
 
 
 
 

In Depth

This project came about because I needed EVE's static data in several web tools I was writing (universe details, blueprint info, etc.) While exploring server-based methods for serving up this data, I realized that the ENTIRE static data export can be reduced to less than 50MB of (gzipped) JSON. Further, most of the data is not used at the same time; the relevant bits that you might use at any one time are routinely less than 500 KB (gzipped) in size. It seemed ridiculous to set up an entire back-end server stack to provide this data for something that could easily be served statically and loaded into a browser where JS can operate on it natively.

layers
 

The EVEoj project is split into 3 layers. Each layer builds upon the next, and each is openly available so that you can integrate at the level that is most relevant for your application. Not at all like onion layers. More like the layers in a stack of tasty, tasty pancakes.

  • Layer 1: an export of the EVE static data to JSON (facilitated by sde2json)
  • Layer 2: EVEoj Core; a JS library to cleanly load and use that data in the browser or Node.js
  • Layer 3: EVEoj Addons; high-level abstractions of data with game mechanics and formulas

Layer 1: JSON export

The first layer is an export of all the EVE static data (YAML and staticdata files) into a consistent JSON format with metainfo. Data is merged into "tables" that are more or less exactly what they sound like. However, since it's JSON, each entry in a table can contain more than just columns; complex data structures are also allowed.

The sde2json tool exports an EVE static data export to the Layer 1 format. While many individuals have helpfully provided SDE exports to different formats in the past, we are at their mercy for updates. In contrast, the sde2json tool is freely available on GitHub. Even if I vanish off the face of the planet, you'll always be able to get your SDE JSON exports by using that tool.

Layer 2: EVEoj Core

The second layer is a JavaScript library that asynchronously loads the static data tables into the browser. Large data sets (such as item details) are further segmented so they can be loaded piecemeal. Currently only an SDD table source is implemented, but I hope to eventually provide CREST and maybe even API sources at this level as well.

The EVEoj core exposes metainfo about tables and provides a consistent interface to access table data. This layer very specifically does NOT try to hide details of the data. Working with the table data exposed at this level is pretty close to working directly with the original EVE static data export.

Layer 3: EVEoj Addons

chevrons
 

The final layer is a set of addon JS modules that rely on the core but abstract away details of the underlying data to provide high-level interfaces. These modules work with concepts that should be familiar to any EVE player. Remember, all of this information is ALREADY available in the tables provided by core at Level 2; you just have to know the details to find it if you don't use these abstractions.

  • .map :: partially implemented
    Mapping information such as region, constellation, and solar system info. Celestial information (gates, belts, stations, etc.). Routes, auto-pilot calculations, and jump distances.
  • .inv :: not implemented
    Inventory and type information, category and group metadata, and icon details.
  • .dogma :: partially implemented
    Ships, modules, skills, effects, and fitting. Mechanics and formulas for motion and damage.
  • .ram :: not implemented
    Blueprints, reactions, PI, and other industrial activities. Mechanics and formulas for research and manufacturing.

Static Data

A major component of this project is to simplify and normalize access to the EVE static data without any need for a dynamic back-end server. The static data export produced by sde2json tallies up to about 48MB of gzipped JSON. Firefox can load and parse the entire data set in about 10s. The point of this project, however, is that you should never need to load the entire data set all at once.

layers
 

The data is split intelligently (I hope) across tables to try and keep the actual working sizes down to around 500 KB or less. Very large data sets (e.g. invItems and celestial statistics) are further segmented to support partial loading. If you know the ID you are looking for, you can do a small partial load to get that entry without loading all 500,000 rows.

CDN

layers
 

Part of what makes using static JSON appealing is the fact that the browser can cache these files so that a user rarely needs to hit the server to reload data. However, caching doesn't work as well if every tool uses its own, separately hosted version of the static data. Never mind that not every author has the capability to host 50MB of static JSON data in their environment.

To that end, I am committed to providing content-delivery network (CDN) hosted versions of the libraries and data. This will continue until I run out of money or regain my sanity. I currently provide hosting for other projects that total about a million hits a month, so I am not completely delusional as to what this entails.

You are free to use the links mentioned as "CDN" in the downloads section, both for the JS library and for the JSON data source of the SDD loader (CORS-enabled).

The CDN links for the JS library have both specific release versions as well as a ".x" version that will load the latest point release for a given version.

layers
 
 

I cannot guarantee that the CDN hosted versions of these files will remain available forever. I will do everything possible to continue providing this service or to work with others to provide this service should it grow beyond my capacity. I of course reserve the right to cut you off at any time, without notice, for any reason, should I need to (both generally, everyone at once, or specifically, just you... yes, YOU... you know who you are).

Regardless, the value of this project is not diminished if the CDN hosted version disappears some day. All of the static data and JS libraries will continue to be made available; you will simply need to provide your own hosting for the data source used by your tools should that day arrive.

Tables

Tables are the basic data representation in EVEoj. They are more or less exactly what they sound like — a table of data indexed by a key — and are most literally just hash maps (e.g. JS objects). Tables in EVEoj have a few differences from a classic RDBMS table, owing to the fact that they are implemented in JSON and combine multiple data sources.

  • EVEoj tables have ONE and ONLY ONE primary key; further, this key must be numeric for segmentation to work
  • each row or entry in a table is simply a JS array of column values
  • column values in a table can contain not just simple values (basic types such as a string or number), but also complex values (JSON arrays or objects)
  • composite keys are handled by nesting hash maps; one key is selected as the primary (top-level) key for the hash map (and segmentation), and each entry points to another hash map with subsequent composite keys as indexes until we reach the array of column data
  • obviously JS is not an RDBMS so there is no SQL for searching tables; instead use Underscore.js
  • likewise, there are no indexes beyond the primary and sub key hash maps; however, the underscore "groupBy" method works very well for dynamically creating indexes on columns

The specific tables available depends entirely on the data source. Let's take a look at the meta information for the tables provided in the CDN hosted JSON data.

Table Data - inspecting the meta info

This is the only time we'll talk to the server, and we're just grabbing static files, so any old web-server will do.

First, we need a static data source. We create one with the 'json' source type configured to use the CDN hosted JSON (CORS-enabled) data.

var SDD = EVEoj.SDD.Create('json', {'path': 'https://cf.eve-oj.com/sdd/' + ver});

Once we have the SDD object, we want its metadata, so we call LoadMeta. This fetches information about the available tables. Since this is an asynchronous operation, it returns a Promise object that is used to add success and failure handlers.

SDD.LoadMeta()
    .then(function(arg){
        var src = arg.source;
        _.each(src.GetTables(), function(tableid){
            var tbl = src.GetTable(tableid);
            // tbl.name ; the table name/ID
            // tbl.length ; the number of rows in the table
            // tbl.segments.length ; the number of segments in the table
            // tbl.columns ; an array containing the column names
            // tbl.keyname ; the primary key for this table
            // tbl.subkeys ; an array of subkeys for this table
            // tbl.colmeta ; additional meta info about complex columns
        });
    });

We use an anonymous success handler on the LoadMeta call to immediately iterate through the tables available in this source. For each table there is a collection of metainfo that we can use for our very own nefarious, world-dominating purposes!

EVEoj Core

core... thingy
 
 
 

The main task of the EVEoj core is to enable the loading of tables from a data source. It parses metainfo about tables provided by the source it's pointed to, and uses this information to expose those tables and load their data as needed. It describes what columns are available, the primary key (and any sub keys), the number of rows, and how the data is segmented. It also tells the source where the table data can be found in the collection of static data files.

The table and metainfo data is generic and could be used to provide data beyond just the static data exports. For instance, putting your jump bridge network or user info into a JSON static file that conforms to this format would allow those tables to be loaded using the EVEoj core as well.

A specific goal of the EVEoj core layer and the tables it loads is that the data should work nicely with Underscore.js. While underscore is not a requirement to use EVEoj, it can definitely make working with the data much more friendly.

Reference

EVEoj.Const

constants
  • EVEoj.VERSION — the version of the library
  • EVEoj.Const.M_per_LY — meters per light year
  • EVEoj.Const.M_per_AU — meters per AU

EVEoj.Utils

FormatNum

EVEoj.Utils.FormatNum(number, decimals)String

Returns a formatted string version of number with commas for thousands separators and a period for the decimal separator. Rounds to the number of decimals specified (use 0 to round to nearest whole integer).


EVEoj.SDD

Data sources for static data (as opposed to the theoretical CREST or API variants that are TOTALLY ON THE WAY I PROMISE! :)

The only current SDD data source implementation is the json type. However, this type supports both an AJAX implementation when running in the browser and a file-system implementation when running in Node.js. The Node.js fs implementation requires the JSON static data files to be stored locally.

Create

EVEoj.SDD.Create(type[, config])EVEoj.SDD.Source

Creates a new EVEoj.SDD.Source object of the type specified. Accepts an optional config object with settings for the source type. Returns null if the specified type has no valid implementation.


EVEoj.SDD.Source

This is the basic interface for all EVEoj.SDD data source implementations.

properties
source
 
  • source.version — the version of the loaded source data
  • source.verdesc — an optional descriptive version string for the loaded source data
  • source.schema — the schema ID for the loaded source data
LoadMeta

source.LoadMeta([opts])Promise

Begins asynchronous loading of the metadata for the source and immediately returns a Promise.

callbacks

then_handlers(arg) — Receives the arg.source object that finished loading. The arg.context contains the optional opts.context provided to the original call.

HasTable

source.HasTable(tableID)Boolean

Tests if the source object contains the specific tableID. Will always return false until a successful LoadMeta has completed on the source.

GetTables

source.GetTables()Array<String>

Returns a list of the tableID's present in this source. Will always return an empty list until a successful LoadMeta has completed.

GetTable

source.GetTable(tableID)EVEoj.SDD.Source.Table

Returns the EVEoj.SDD.Source.Table object in this source with the tableID provided. Returns null if no such table exists in the source (or if LoadMeta has not yet completed).


EVEoj.SDD.Source.json

This is the implementation of the json SDD source type. When creating this source type, the following settings can be provided as properties in a config object:

  • path - required: the path to the JSON data (for Node.js, this is a local file system path -- no other settings apply for Node.js)
  • datatype - the source type; "json" (default) is only supported value
  • cache - whether the AJAX requests should be cached; defaults to true
  • timeout - the AJAX timeout to use; defaults to 0

EVEoj.SDD.Table

The table is the basic data access abstraction in the EVEoj core. Tables can only be retrieved from instantiated EVEoj.SDD.Source objects.

properties
  • table.src — the EVEoj.SDD.Source that created this table
  • table.name — the name/ID of this table
  • table.keyname — the name of the primary key column
  • table.subkeys — an array of additional subkeys (or an empty array if none)
  • table.columns — an array of column names for each entry in the table
  • table.colmap — a reverse lookup hash mapping column names to column indexes
  • table.c — a shorter name for table.colmap
  • table.colmeta — a hash (indexed by column name) of additional information for complex column structures; this data is informative only, and may not be accurate (or even exist) for any given column
  • table.length — the number of rows in this table
  • table.loaded — the number of rows LOADED into this table (from 0 to table.length)
  • table.segment — an array of segment info objects for the table
  • table.data — the data object for this table, described in detail below
CONCORD
 
 
 

The table.data property is the primary means of interacting with the table data. It is a hash map indexed by the table.keyname. If no table.subkeys exist, then each entry in the hash map will be an Array containing the column values for that entry. The column values for each entry can be retrieved by index, and the index for a specific column name can be looked up in the table.colmap.

If table.subkeys DO exist, then each top-level entry in the table.data hash will not be an Array, but rather another hash map that is indexed by the first key in table.subkeys. Each of THOSE entries will ALSO be hash maps indexed by additional table.subkeys, nesting down until you run out of subkeys. The last entry in the nesting will then be an Array containing the column values for that entry.

Remember that each column value for an entry may itself be a complex JS Array or Object of its own with additional properties. Or, the value may be a simple base type. If a column contains a complex data type, the table.colmeta hash should hopefully contain an entry for that column with additional information about what to expect. This is purely optional and for human consumption only, and not always accurate (or even present).

Load

table.Load([opts])Promise

Begins asynchronous loading of the table data and immediately returns a Promise. The optional opts.key property can be used to specify a key to look for. If a specific opts.key is provided, and the table has segments, ONLY the segment containing that key will be loaded. If no opts.key is provided then all table segments are loaded. An optional callback can be provided in opts.progress to receive progress updates for the load.

callbacks

then_handlers(arg) — Receives the arg.table object that finished loading. The arg.context contains the optional opts.context provided to the original call.

progress_callback(arg) — The progress callback receives the arg.table object that is currently loading, the number of segments it arg.has finished loading, and the total number of segments it arg.needs to load (this is the total count required, including those segments already loaded). It also gets an arg.context from the optional opts.context provided to the original call. There is no guarantee that a progress callback will be called for each segment, or even that it will ever be called prior to the completion of the Load promise.

GetEntry

table.GetEntry(ID)Array or Object

Returns the entry indexed by the ID provided, if it exists in the table. If the segment that contains this entry has NOT yet been loaded, it will return false. If the segment that contains this entry HAS been loaded and there is no matching entry, it will return null. Use strict equivalence tests to differentiate these scenarios.

The only reason to use this function rather than accessing table.data directly is to test for the existence vs. loaded distinction for an entry, as described above. Note that there is no way to know if an ID maps to a valid entry in the table until after the segment that should contain that ID has been loaded.

GetValue

table.GetValue(ID, column) → column data

Returns the value for the named or indexed column (auto-determined based on use of a numeric or string value) from the entry in the table indexed by the ID provided. This is merely a convenience method for simple, single-value lookups. Note that this returns null or false as per the same logic in GetEntry whether the ID is loaded into the table or not. HOWEVER, there is no way to distinguish between those values and a valid entry with a column that actually contains a null or false value.

ColIter

table.ColIter(column)function(entry)

Returns an iterator function that accepts a single argument — the entry to process — and returns the value in the column specified (auto-index-detection based on whether a numeric or string is provided). This is a convenience method to get an iterator for the common use-case of applying Underscore functions to a column value on the table.data list (such as groupBy).

ColPred

table.ColPred(column, comparison, value)function(entry)

Returns a predicate function that accepts a single argument — the entry to process — and returns a boolean result by comparing the value in the column specified (auto-index-detection based on whether a numeric or string is provided) with the value provided. The type of comparison to use is provided as a string and can be ==, !=, ===, !==, <, <=, >, or >=. This is a convenience method to get a predicate for the common use-case of applying Underscore functions to a column value on the table.data list (such as filter).

EVEoj.map

map
 

A map provides an abstraction over regions, constellations, solar systems, and celestial info, as well as route planning, distance calculations, and more.

Reference

EVEoj.map

properties
  • map.loaded — whether this map has been fully loaded or not
Create

EVEoj.map.Create(source, type[, config])EVEoj.map

Creates a new map object using the SDD data source provided. The map type should be one of 'K' (known space), 'X' (unknown/Jove space), or 'J' (wormhole space). An optional config object can also be provided with specific settings for this map.

K- and X-space config settings

All of these config options default to false.

  • config.jumps — include additional jump data (not needed for basic route calculations)
  • config.planets — include planet celestial data
  • config.moons — moon celestial data
  • config.belts — belt celestial data
  • config.gates — jumpgate celestial data
  • config.stars — star celestial data
  • config.objects — celestial object reverse lookup (by solar system)
  • config.landmarks — landmark data
J-space config settings

All of these config options default to false.

  • config.planets — include planet celestial data
  • config.moons — moon celestial data
  • config.stars — star celestial data
  • config.objects — celestial object reverse lookup (by solar system)
Load

map.Load([opts])Promise

Begins asynchronous loading of the map data from the source provided during object creation, then immediately returns a Promise. An optional callback can be provided in opts.progress to receive progress updates for the load.

callbacks

then_handlers(arg) — Receives the arg.table object that finished loading. The arg.context contains the optional opts.context provided to the original call.

progress_callback(arg) — The progress callback receives the arg.map object that is currently loading, the number of segments it arg.has finished loading, and the total number of segments it arg.needs to load (this is the total count required, including those segments already loaded). It also gets an arg.context from the optional opts.context provided to the original call. There is no guarantee that a progress callback will be called for each segment, or even that it will ever be called prior to the completion of the Load promise.

GetSystem

map.GetSystem(system)EVEoj.map.System

Gets an EVEoj.map.System object for the system provided (or null if no such system is found). The system provided may be either a numeric systemID # or a system name (case-sensitive).

GetSystems

map.GetSystems()EVEoj.map.SystemIter

Gets a system iterator that lets you iterate through every system contained in the map.

JumpDist

map.JumpDist(fromSystemID, toSystemID)Number

Gets the jump distance in light years between the fromSystemID and the toSystemID.

Route

map.Route(fromSystemID, toSystemID, avoidList, avoidLow, avoidHi)Array<systemID>

route thingy
 

Gets an array containing the list of systemID's on the route connecting fromSystemID and toSystemID. A list of systemID's to avoid can be provided as an array to avoidList (use an empty array for no avoids). Note that this is a hard avoidance; if no route exists that does NOT contain a system in the avoidList, an empty route will be returned. The avoidLow and avoidHi values should be set to true or false as desired. Unlike the avoidList, these are soft avoids and valid routes will be returned even if it violates the avoidLow/Hi preference. Set both to false to simply find the shortest possible route.


EVEoj.map.System

properties
  • system.ID — the system ID #
  • system.name — the system name
  • system.regionID — the region ID this system is in
  • system.constellationID — the constellation ID this system is in
  • system.pos.x|y|z — the x, y, and z coordinate for this system
  • system.posMax.x|y|z — the x, y, and z max coordinate for this system
  • system.posMin.x|y|z — the x, y, and z min coordinate for this system
  • system.border
  • system.fringe
  • system.corridor
  • system.hub
  • system.international
  • system.regional
  • system.constellation
  • system.contiguous — if this system is part of contiguous hisec (connected to Jita by hisec route)
  • system.security — the true security value for the system
  • system.sec — the rounded/in-game security for the system
  • system.securityClass
  • system.wormholeClassID
  • system.stationCount — the number of stations in the system
  • system.jumps - an array of destination system IDs with gate jumps from this system

EVEoj.map.SystemIter

An iterator for systems contained in a map.

HasNext

iter.HasNext()Boolean

Whether there are any more systems left in this iterator.

Next

iter.Next()EVEoj.map.System

Returns the next EVEoj.map.System object in the iterator.

EVEoj.inv

Coming soon. Like... CCP "soon".

EVEoj.dogma

Coming soon. Partially implemented but not ported to new lib structure.

EVEoj.ram

Coming really soon because... Crius.

sde2json

This is a Node.js tool that turns an EVE static data export into JSON data. It also creates gzipped variants for ease of use with static hosting (those additional generators can be disabled).

This tool handles only the new full YAML and staticdata SDE, not the legacy SQL/DB format.

See the GitHub project for source and details.

usage: node --expose-gc index.js <--sde data_dir> [opts]

options
  • --sde data_dirrequired: path to the EVE static data export files (the location where the .yaml, .staticdata, etc. files are located)
  • --out sdd_dir — path of the output static data; defaults to {data_dir}/sdd
  • --prefix prefix — specify a specific table space prefix to create (none specified == all)
  • --gzip — set to 0 to disable gzip creation

Info and Links

This is project authored by me, nezroy.

For discussion and feedback, please use the EVE Online forum thread for this project.

For issues and bugs, please refer to the GitHub issue tracker.

-->