diff --git a/code/portal_data.js b/code/portal_data.js new file mode 100644 index 00000000..867bb464 --- /dev/null +++ b/code/portal_data.js @@ -0,0 +1,41 @@ +/// PORTAL DATA TOOLS /////////////////////////////////////////////////// +// misc functions to get portal info + +// search through the links data for all that link from or to a portal. returns an object with separate lists of in +// and out links. may or may not be as accurate as the portal details, depending on how much data the API returns +window.getPortalLinks = function(guid) { + + var links = { in: [], out: [] }; + + $.each(window.links, function(g,l) { + var d = l.options.data; + + if (d.oGuid == guid) { + links.out.push(g); + } + if (d.dGuid == guid) { + links.in.push(g); + } + }); + + return links; +} + + +// search through the fields for all that reference a portal +window.getPortalFields = function(guid) { + var fields = []; + + $.each(window.fields, function(g,f) { + var d = f.options.data; + + if ( d.points[0].guid == guid + || d.points[1].guid == guid + || d.points[2].guid == guid ) { + + fields.push(g); + } + }); + + return fields; +} diff --git a/code/portal_detail_display.js b/code/portal_detail_display.js index ef5eb31b..3c31cb07 100644 --- a/code/portal_detail_display.js +++ b/code/portal_detail_display.js @@ -3,81 +3,42 @@ // methods that highlight the portal in the map view. window.renderPortalDetails = function(guid) { + // TODO: start a request for the selected portal detailed data + // (do this even if not in window.portals?) + selectPortal(window.portals[guid] ? guid : null); + if(!window.portals[guid]) { urlPortal = guid; $('#portaldetails').html(''); if(isSmartphone()) { $('.fullimg').remove(); - $('#mobileinfo').html(''); + $('#mobileinfo').html('
tap here for info screen
'); } return; } - var d = window.portals[guid].options.details; - - // collect some random data that’s not worth to put in an own method - var links = {incoming: 0, outgoing: 0}; - if(d.portalV2.linkedEdges) $.each(d.portalV2.linkedEdges, function(ind, link) { - links[link.isOrigin ? 'outgoing' : 'incoming']++; - }); - function linkExpl(t) { return ''+t+''; } - var linksText = [linkExpl('links'), linkExpl(' ↳ ' + links.incoming+'  •  '+links.outgoing+' ↴')]; - - var player = d.captured && d.captured.capturingPlayerId - ? '' + getPlayerName(d.captured.capturingPlayerId) + '' - : null; - var playerText = player ? ['owner', player] : null; - - var time = d.captured - ? '' - + unixTimeToString(d.captured.capturedTime) + '' - : null; - var sinceText = time ? ['since', time] : null; - - var linkedFields = ['fields', d.portalV2.linkedFields ? d.portalV2.linkedFields.length : 0]; - - // collect and html-ify random data - var randDetails = [ - playerText, sinceText, - getRangeText(d), getEnergyText(d), - linksText, getAvgResoDistText(d), - linkedFields, getAttackApGainText(d), - getHackDetailsText(d), getMitigationText(d) - ]; - - // artifact details - - //niantic hard-code the fact it's just jarvis shards/targets - so until more examples exist, we'll do the same - //(at some future point we can iterate through all the artifact types and add rows as needed) - var jarvisArtifact = artifact.getPortalData (guid, 'jarvis'); - if (jarvisArtifact) { - // the genFourColumnTable function below doesn't handle cases where one column is null and the other isn't - so default to *something* in both columns - var target = ['',''], shards = ['shards','(none)']; - if (jarvisArtifact.target) { - target = ['target', ''+(jarvisArtifact.target==TEAM_RES?'Resistance':'Enlightened')+'']; - } - if (jarvisArtifact.fragments) { - shards = [jarvisArtifact.fragments.length>1?'shards':'shard', '#'+jarvisArtifact.fragments.join(', #')]; - } - - randDetails.push (target, shards); - } + var portal = window.portals[guid]; + var data = portal.options.data; + var details = undefined; //TODO: get the details - randDetails = '' + genFourColumnTable(randDetails) + '
'; + var modDetails = details ? '
'+getModDetails(details)+'
' : ''; + var randDetails = details ? getPortalRandDetails(details) : ''; + var resoDetails = details ? getResonatorDetails(details) : ''; - var resoDetails = '' + getResonatorDetails(d) + '
'; - var img = getPortalImageUrl(d); - var lat = d.locationE6.latE6/1E6; - var lng = d.locationE6.lngE6/1E6; - var perma = '/intel?ll='+lat+','+lng+'&z=17&pll='+lat+','+lng; - var imgTitle = 'title="'+getPortalDescriptionFromDetails(d)+'\n\nClick to show full image."'; - var poslinks = 'window.showPortalPosLinks('+lat+','+lng+',\''+escapeJavascriptString(d.portalV2.descriptiveText.TITLE)+'\')'; - var portalDetailObj = window.getPortalDescriptionFromDetailsExtended(d); + + + var img = fixPortalImageUrl(details ? details.imageByUrl && details.imageByUrl.imageUrl : data.image); + var title = details ? details.portalV2.descriptiveText.TITLE : data.title; + + var lat = data.latE6/1E6; + var lng = data.lngE6/1E6; + + var imgTitle = ''; //'title="'+getPortalDescriptionFromDetails(details)+'\n\nClick to show full image."'; + var portalDetailObj = undefined; //window.getPortalDescriptionFromDetailsExtended(details); var portalDetailedDescription = ''; @@ -111,71 +72,182 @@ window.renderPortalDetails = function(guid) { portalDetailedDescription += ''; } - var levelDetails = getPortalLevel(d); - if(levelDetails != 8) { - if(levelDetails==Math.ceil(levelDetails)) - levelDetails += "\n8"; - else - levelDetails += "\n" + (Math.ceil(levelDetails) - levelDetails)*8; - levelDetails += " resonator level(s) needed for next portal level"; - } else { - levelDetails += "\nfully upgraded"; + // portal level. start with basic data - then extend with fractional info in tooltip if available + var levelInt = data ? data.level : getPortalLevel(details); + var levelDetails = data.level; + if (details) { + levelDetails = getPortalLevel(details); + if(levelDetails != 8) { + if(levelDetails==Math.ceil(levelDetails)) + levelDetails += "\n8"; + else + levelDetails += "\n" + (Math.ceil(levelDetails) - levelDetails)*8; + levelDetails += " resonator level(s) needed for next portal level"; + } else { + levelDetails += "\nfully upgraded"; + } } levelDetails = "Level " + levelDetails; + + var linkDetails = []; + + var posOnClick = 'window.showPortalPosLinks('+lat+','+lng+',\''+escapeJavascriptString(title)+'\')'; + var permalinkUrl = '/intel?ll='+lat+','+lng+'&z=17&pll='+lat+','+lng; + + if (typeof android !== 'undefined' && android && android.intentPosLink) { + // android devices. one share link option - and the android app provides an interface to share the URL, + // share as a geo: intent (navigation via google maps), etc + + var shareLink = $('
').html( $('').attr({onclick:posOnClick}).text('Share portal') ).html(); + linkDetails.push(''); + + } else { + // non-android - a permalink for the portal + var permaHtml = $('
').html( $('').attr({href:permalinkUrl, target:'_blank', title:'Create a URL link to this portal'}).text('Portal link') ).html(); + linkDetails.push ( '' ); + + // and a map link popup dialog + var mapHtml = $('
').html( $('').attr({onclick:posOnClick, title:'Link to alternative maps (Google, etc)'}).text('Map links') ).html(); + linkDetails.push(''); + + } + $('#portaldetails') - .attr('class', TEAM_TO_CSS[getTeam(d)]) - .html('' - + '

'+escapeHtmlSpecialChars(d.portalV2.descriptiveText.TITLE)+'

' - + 'X' + .html('') //to ensure it's clear + .attr('class', TEAM_TO_CSS[portal.options.team]) + .append( + $('

').attr({class:'title'}).text(data.title), + + $('').attr({class:'close', onclick:'renderPortalDetails(null); if(isSmartphone()) show("map");',title:'Close'}).text('X'), + // help cursor via ".imgpreview img" - + '
' - + ''+Math.floor(getPortalLevel(d))+'' - + '
'+ portalDetailedDescription + '
' - + '
' - + '

' - + '
'+getModDetails(d)+'
' - + randDetails - + resoDetails - + '
' + $('
') + .attr({class:'imgpreview', title:imgTitle, style:"background-image: url('"+img+"')"}) + .append( + $('').attr({id:'level', title: levelDetails}).text(levelInt), + $('
').attr({class:'portalDetails'}).html(portalDetailedDescription), + $('').attr({class:'hide', src:img}) + ), + + modDetails, + + randDetails, + + resoDetails, + + '
' + linkDetails.join('') + '
' ); - runHooks('portalDetailsUpdated', {portalDetails: d}); + runHooks('portalDetailsUpdated', {guid: guid, portal: portal, portalDetails: details, portalData: data}); } + + +window.getRandPortalDetails = function(d) { + + var randDetails; + + if (d) { + + // collect some random data that’s not worth to put in an own method + var links = {incoming: 0, outgoing: 0}; + $.each(d.portalV2.linkedEdges||[], function(ind, link) { + links[link.isOrigin ? 'outgoing' : 'incoming']++; + }); + + function linkExpl(t) { return ''+t+''; } + var linksText = [linkExpl('links'), linkExpl(' ↳ ' + links.incoming+'  •  '+links.outgoing+' ↴')]; + + var player = d.captured && d.captured.capturingPlayerId + ? '' + getPlayerName(d.captured.capturingPlayerId) + '' + : null; + var playerText = player ? ['owner', player] : null; + + var time = d.captured + ? '' + + unixTimeToString(d.captured.capturedTime) + '' + : null; + var sinceText = time ? ['since', time] : null; + + var linkedFields = ['fields', d.portalV2.linkedFields ? d.portalV2.linkedFields.length : 0]; + + // collect and html-ify random data + var randDetailsData = []; + if (playerText && sinceText) { + randDetailsData.push (playerText, sinceText); + } + + randDetailsData.push ( + getRangeText(d), getEnergyText(d), + linksText, getAvgResoDistText(d), + linkedFields, getAttackApGainText(d), + getHackDetailsText(d), getMitigationText(d) + ); + + // artifact details + + //niantic hard-code the fact it's just jarvis shards/targets - so until more examples exist, we'll do the same + //(at some future point we can iterate through all the artifact types and add rows as needed) + var jarvisArtifact = artifact.getPortalData (guid, 'jarvis'); + if (jarvisArtifact) { + // the genFourColumnTable function below doesn't handle cases where one column is null and the other isn't - so default to *something* in both columns + var target = ['',''], shards = ['shards','(none)']; + if (jarvisArtifact.target) { + target = ['target', ''+(jarvisArtifact.target==TEAM_RES?'Resistance':'Enlightened')+'']; + } + if (jarvisArtifact.fragments) { + shards = [jarvisArtifact.fragments.length>1?'shards':'shard', '#'+jarvisArtifact.fragments.join(', #')]; + } + + randDetailsData.push (target, shards); + } + + randDetails = '' + genFourColumnTable(randDetailsData) + '
'; + + } + + return randDetails; +} + + // draws link-range and hack-range circles around the portal with the // given details. Clear them if parameter 'd' is null. -window.setPortalIndicators = function(d) { +window.setPortalIndicators = function(p) { if(portalRangeIndicator) map.removeLayer(portalRangeIndicator); portalRangeIndicator = null; if(portalAccessIndicator) map.removeLayer(portalAccessIndicator); portalAccessIndicator = null; - if(d === null) return; + // if we have a portal... - var range = getPortalRange(d); - var coord = [d.locationE6.latE6/1E6, d.locationE6.lngE6/1E6]; - portalRangeIndicator = (range.range > 0 - ? L.geodesicCircle(coord, range.range, { - fill: false, - color: RANGE_INDICATOR_COLOR, - weight: 3, - dashArray: range.isLinkable ? undefined : "10,10", - clickable: false }) - : L.circle(coord, range.range, { fill: false, stroke: false, clickable: false }) + if(p) { + var coord = p.getLatLng(); + + // range is only known for sure if we have portal details + // TODO? render a min range guess until details are loaded..? + + var d = p.options.details; + if (d) { + var range = getPortalRange(d); + portalRangeIndicator = (range.range > 0 + ? L.geodesicCircle(coord, range.range, { + fill: false, + color: RANGE_INDICATOR_COLOR, + weight: 3, + dashArray: range.isLinkable ? undefined : "10,10", + clickable: false }) + : L.circle(coord, range.range, { fill: false, stroke: false, clickable: false }) + ).addTo(map); + } + + portalAccessIndicator = L.circle(coord, HACK_RANGE, + { fill: false, color: ACCESS_INDICATOR_COLOR, weight: 2, clickable: false } ).addTo(map); + } - portalAccessIndicator = L.circle(coord, HACK_RANGE, - { fill: false, color: ACCESS_INDICATOR_COLOR, weight: 2, clickable: false } - ).addTo(map); } // highlights portal with given GUID. Automatically clears highlights diff --git a/code/portal_detail_display_tools.js b/code/portal_detail_display_tools.js index 353189d9..e3c67998 100644 --- a/code/portal_detail_display_tools.js +++ b/code/portal_detail_display_tools.js @@ -184,7 +184,8 @@ window.getResonatorDetails = function(d) { resoDetails.push(renderResonatorDetails(slot, l, v, dist, nick)); }); - return genFourColumnTable(resoDetails); + return '' + genFourColumnTable(resoDetails) + '
'; + } // helper function that renders the HTML for a given resonator. Does diff --git a/code/portal_info.js b/code/portal_info.js index fd27b4ee..5a85d4ee 100644 --- a/code/portal_info.js +++ b/code/portal_info.js @@ -194,10 +194,8 @@ window.potentialPortalLevel = function(d) { } -window.getPortalImageUrl = function(d) { - if (d.imageByUrl && d.imageByUrl.imageUrl) { - url = d.imageByUrl.imageUrl; - +window.fixPortalImageUrl = function(url) { + if (url) { if (window.location.protocol === 'https:') { url = url.indexOf('www.panoramio.com') !== -1 ? url.replace(/^http:\/\/www/, 'https://ssl').replace('small', 'medium')