├── LICENSE.txt ├── README.md ├── index.html ├── marketDisplay.css └── marketExplorer.js /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Portions of marketExplorer are taken from https://github.com/jimpurbrick/crestexplorerjs/blob/master/crestexplorer.js, where they are under the MIT and GPL2 licenses. 2 | 3 | 4 | For everything else: 5 | 6 | Copyright (c) 2015 Steve Anderson 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a fairly basic viewer for Eve's Market data, from CREST. 2 | 3 | You'll need to register an application on https://developers.eveonline.com/ with the callback url pointing at the directory you put this in (or at whatever you rename index.html to) 4 | You'll also need to update the clientid to your applications id, and update the callback url 5 | 6 | 7 | 8 | This depends on xdr.js (I got mine from https://raw.githubusercontent.com/jaubourg/ajaxHooks/master/src/xdr.js) and moment.js (http://momentjs.com/) to be in the same directory 9 | 10 | 11 | I have a copy up and running at: https://www.fuzzwork.co.uk/market/viewer2/ 12 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Market Browser 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | Login to Start Browsing the Market 32 | 40 |
41 |
42 |
43 | 46 |
47 |
48 | 50 |
51 | 52 |
53 |
54 |
55 |
56 |
57 |

Sellers

58 | 59 | 60 | 61 | 62 | 63 | 64 |
StationVolumePriceExpiration
65 |
66 |
67 |

Buyers

68 | 69 | 70 | 71 | 72 | 73 | 74 |
StationVolumeMin VolumePriceRangeExpiration
75 |
76 |
77 |
78 | 79 | 80 | -------------------------------------------------------------------------------- /marketDisplay.css: -------------------------------------------------------------------------------- 1 | body 2 | { 3 | font: 1em "Trebuchet MS",sans-serif; 4 | background: black url(https://community.eveonline.com/bitmaps/interface/community/bg.jpg) top right no-repeat; 5 | background-attachment: fixed; 6 | color: rgb(200,200,200); 7 | height:100%; 8 | width:100%; 9 | } 10 | html { 11 | min-height:100%;/* make sure it is at least as tall as the viewport */ 12 | width:100%; 13 | position:relative 14 | } 15 | 16 | ul 17 | { 18 | list-style-type: none; 19 | } 20 | 21 | li { 22 | display:block; 23 | } 24 | 25 | .groupLink 26 | { 27 | padding:2px; 28 | margin:2px; 29 | background-color: rgba(83, 83, 83, 0.95); 30 | border: 0 solid black; 31 | border-bottom:1px; 32 | border-top:1px; 33 | } 34 | 35 | 36 | .marketgroupmain 37 | { 38 | position:fixed; 39 | width:98%; 40 | height:97%; 41 | margin: 5px; 42 | padding:5px; 43 | background-color: rgba(43, 43, 43, 0.95); 44 | border-radius: 2px; 45 | } 46 | 47 | #login-window { 48 | display: inline-block; 49 | border: 2px solid; 50 | border-radius: 25px; 51 | border-color: rgb(20,20,20); 52 | padding:10px; 53 | background-color: rgba(43, 43, 43, 0.95); 54 | } 55 | #login { 56 | color: rgb(200,200,200); 57 | } 58 | .marketgroupdiv { 59 | position:relative; 60 | overflow-x: hidden; 61 | overflow-y: auto; 62 | max-height:90%; 63 | width:25%; 64 | max-width:25% 65 | } 66 | 67 | .marketdisplaydiv { 68 | position:absolute; 69 | overflow: auto; 70 | max-height:90%; 71 | width:70%; 72 | max-width:70%; 73 | left:26%; 74 | top:3%; 75 | padding-left:5px; 76 | } 77 | -------------------------------------------------------------------------------- /marketExplorer.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Sections of code from https://github.com/jimpurbrick/crestexplorerjs 3 | * Copyright 2012, CCP (http://www.ccpgames.com) 4 | * Dual licensed under the MIT or GPL Version 2 licenses. 5 | * http://www.opensource.org/licenses/mit-license.php 6 | * http://www.opensource.org/licenses/GPL-2.0 7 | * 8 | * All other code is under the MIT license. 9 | * 10 | */ 11 | 12 | 13 | var endpoints; 14 | var regions=Array(); 15 | var marketGroups; 16 | var currentRegion; 17 | var currentGroup; 18 | 19 | (function ($, window, document) { 20 | 21 | "use strict"; 22 | 23 | // Configuration parameters 24 | var redirectUri = "https://www.fuzzwork.co.uk/market/viewer2/"; 25 | var clientId = "f8c9bd2520064883ba1af73fafc1e486"; // OAuth client id 26 | var csrfTokenName = clientId + "csrftoken"; 27 | var hashTokenName = clientId + "hash"; 28 | var scopes = "publicData"; 29 | var baseURL = "https://crest-tq.eveonline.com"; 30 | 31 | 32 | 33 | 34 | // Show error message in main data pane. 35 | function displayError(error) { 36 | $("#data").children().replaceWith("" + error + ""); 37 | } 38 | 39 | // Request uri and render as HTML. 40 | function render(uri) { 41 | if (uri.indexOf("http") !== 0) { 42 | displayError("Addresses must be absolute"); 43 | return; 44 | } 45 | $.getJSON(uri, function(data, status, xhr) { 46 | $("#data").children().replaceWith(buildElement(data)); 47 | bindLinks(); 48 | }); 49 | } 50 | 51 | function loadEndpoints() { 52 | $.getJSON(baseURL,function(data,status,xhr) { 53 | window.endpoints=data; 54 | loadRegions(); 55 | loadMarketGroups(); 56 | }); 57 | 58 | } 59 | 60 | function loadRegions() { 61 | 62 | $.getJSON(window.endpoints["regions"].href,function(data,status,xhr) { 63 | $.each(data['items'],function(index,value){ 64 | if (!value.name.match('.-R00')) { 65 | window.regions[value.name]=value.href; 66 | $("#regionSelector").append(""); 67 | } 68 | }); 69 | $("#regionSelect").show(); 70 | }); 71 | 72 | } 73 | 74 | function loadMarketGroups() { 75 | $.getJSON(window.endpoints["marketGroups"].href,function(data,status,xhr) { 76 | marketGroups=data.items; 77 | $.map(marketGroups,function(group){ 78 | if (typeof group.parentGroup === 'undefined') { 79 | $("#marketGroups").append(""); 80 | } 81 | } 82 | ); 83 | $('.groupLink').click(function(event){event.stopPropagation();openSubGroup(event.target);}); 84 | $("#marketgroupmain").show(); 85 | }); 86 | 87 | 88 | } 89 | 90 | function openSubGroup(group) 91 | { 92 | var node; 93 | var itemcount=0; 94 | if ($(group).children('ul').length>0) { 95 | $(group).children('ul').toggle(); 96 | } else { 97 | $(group).append(''); 98 | node=$(group).children('ul'); 99 | $.map(marketGroups,function(subgroup){ 100 | if (typeof subgroup.parentGroup != 'undefined' && subgroup.parentGroup.href === group.dataset.cresthref) { 101 | node.append(""); 102 | } 103 | if (subgroup.href === group.dataset.cresthref) { 104 | $.getJSON(subgroup.types.href,function(data,status,xhr) { 105 | $.map(data.items,function(item){ 106 | if (item.marketGroup.href== group.dataset.cresthref) { 107 | node.append(""); 108 | itemcount++; 109 | } 110 | }); 111 | console.log(itemcount); 112 | if (itemcount>0) { 113 | console.log('items only'); 114 | $('.itemLink').click(function(event){event.stopPropagation();openItem(event.target);}); 115 | } 116 | }); 117 | } 118 | }); 119 | } 120 | } 121 | 122 | function openItem(item) 123 | { 124 | var buytable; 125 | var selltable; 126 | $('#MarketDisplay').show(); 127 | console.log(item.dataset.cresthref); 128 | $.getJSON(item.dataset.cresthref,function(data,status,xhr) { 129 | $('#itemDescription').html(data.name+"
"+data.description); 130 | }); 131 | if (typeof currentRegion != 'undefined') { 132 | buytable=$('#buy').DataTable(); 133 | buytable.rows().remove(); 134 | selltable=$('#sell').DataTable(); 135 | selltable.rows().remove(); 136 | $.getJSON(currentRegion.marketBuyOrders.href+'?type='+item.dataset.cresthref,function(data,status,xhr) { 137 | $.map(data.items,function(item){ 138 | buytable.row.add([item.location.name,$.number(item.volume_str),item.minVolume_str,$.number(item.price,2),item.range,moment(item.issued).add(item.duration,'days').format("YYYY-MM-DD HH:mm")]); 139 | }); 140 | buytable.draw(); 141 | 142 | }) 143 | $.getJSON(currentRegion.marketSellOrders.href+'?type='+item.dataset.cresthref,function(data,status,xhr) { 144 | $.map(data.items,function(item){ 145 | selltable.row.add([item.location.name,$.number(item.volume_str),$.number(item.price,2),moment(item.issued).add(item.duration,'days').format("YYYY-MM-DD HH:mm")]); 146 | }); 147 | selltable.draw(); 148 | }); 149 | } else { 150 | alert('Set a region to get data'); 151 | } 152 | 153 | } 154 | 155 | function setLanguage() { 156 | var cookieok=confirm("This needs a cookie"); 157 | if (cookieok){ 158 | if ($("#language").val()=="Default") { 159 | $.cookie('market-language',null); 160 | } else { 161 | $.cookie('market-language',$("#language").val()); 162 | } 163 | } 164 | } 165 | 166 | function loadRegionData() { 167 | $.getJSON($("#regionSelector").val(),function(data,status,xhr) { 168 | currentRegion=data; 169 | }); 170 | } 171 | 172 | 173 | // Send Oauth token request on login, reset ajax Authorization header on logout. 174 | function onClickLogin(evt) { 175 | evt.preventDefault(); 176 | var command = $("#login").text(); 177 | if (command === "Login to Start Browsing the Market") { 178 | 179 | // Store CSRF token and current location as cookie 180 | var csrfToken = uuidGen(); 181 | $.cookie(csrfTokenName, csrfToken); 182 | $.cookie(hashTokenName, window.location.hash); 183 | 184 | // No OAuth token, request one from the OAuth authentication endpoint 185 | window.location = "https://login.eveonline.com/oauth/authorize/" + 186 | "?response_type=token" + 187 | "&client_id=" + clientId + 188 | "&scope=" + scopes + 189 | "&redirect_uri=" + redirectUri + 190 | "&state=" + csrfToken; 191 | 192 | } else { 193 | ajaxSetup(false); 194 | loginSetup(false); 195 | } 196 | } 197 | 198 | // Extract value from oauth formatted hash fragment. 199 | function extractFromHash(name, hash) { 200 | var match = hash.match(new RegExp(name + "=([^&]+)")); 201 | return !!match && match[1]; 202 | } 203 | 204 | // Generate an RFC4122 version 4 UUID 205 | function uuidGen() { 206 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 207 | var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 208 | return v.toString(16); 209 | }); 210 | } 211 | 212 | function ajaxSetup(token) { 213 | var headers = { 214 | "Accept": "application/json, charset=utf-8" 215 | }; 216 | if (token) { 217 | headers.Authorization = "Bearer " + token; 218 | } 219 | if ($.cookie('market-language')) { 220 | headers['Accept-Language'] = $.cookie('market-language'); 221 | } 222 | $.ajaxSetup({ 223 | accepts: "application/json, charset=utf-8", 224 | crossDomain: true, 225 | type: "GET", 226 | dataType: "json", 227 | headers: headers, 228 | error: function (xhr, status, error) { 229 | displayError(error); 230 | } 231 | }); 232 | } 233 | 234 | function loginSetup(token) { 235 | $("#login").click(onClickLogin); 236 | } 237 | 238 | $(document).ready(function() { 239 | 240 | var hash = window.location.hash; 241 | var token = extractFromHash("access_token", hash); 242 | $('#buy').DataTable({ 243 | "paging": false, 244 | "scrollY": "40%", 245 | "bFilter": false, 246 | "bInfo": false, 247 | "bAutoWidth": false, 248 | "bSortClasses": false, 249 | "bDeferRender": false, 250 | "sDom": 'C<"clear">lfrtip', 251 | "order":[[3,"desc"]], 252 | "columnDefs": [ 253 | { className: "dt-left"}, 254 | { className: "dt-right"}, 255 | { className: "dt-right"}, 256 | { className: "dt-right"}, 257 | { className: "dt-right"}, 258 | { className: "dt-right"} 259 | ] 260 | 261 | }); 262 | $('#sell').DataTable({ 263 | "paging": false, 264 | "scrollY": "40%", 265 | "bFilter": false, 266 | "bInfo": false, 267 | "bAutoWidth": false, 268 | "bSortClasses": false, 269 | "bDeferRender": false, 270 | "sDom": 'C<"clear">lfrtip', 271 | "order":[[2,"asc"]], 272 | "columnDefs": [ 273 | { className: "dt-left"}, 274 | { className: "dt-right"}, 275 | { className: "dt-right"}, 276 | { className: "dt-right"}, 277 | { className: "dt-right"} 278 | ] 279 | }); 280 | 281 | $("#MarketDisplay").hide(); 282 | 283 | if (token) { 284 | ajaxSetup(token); 285 | // Check CSRF token in state matches token saved in cookie 286 | if(extractFromHash("state", hash) !== $.cookie(csrfTokenName)) { 287 | displayError("CSRF token mismatch"); 288 | return; 289 | } 290 | 291 | // Restore hash. 292 | window.location.hash = $.cookie(hashTokenName); 293 | 294 | // Delete cookies. 295 | $.cookie(csrfTokenName, null); 296 | $.cookie(hashTokenName, null); 297 | $("#login-window").hide(); 298 | loadEndpoints(); 299 | } else { 300 | $("#regionSelect").hide(); 301 | $(".marketgroupmain").hide(); 302 | } 303 | 304 | loginSetup(token); 305 | $("#regionSelector").change(function() { 306 | loadRegionData(); 307 | }) 308 | $("#language").change(function() { 309 | setLanguage(); 310 | }) 311 | }); 312 | 313 | 314 | }($, window, document)); // End crestexplorerjs 315 | --------------------------------------------------------------------------------