From 8ac130511025ac3f812d183f5c99f058c9ccb29d Mon Sep 17 00:00:00 2001 From: fkloft Date: Sat, 11 Apr 2015 23:45:09 +0200 Subject: [PATCH] [missions] use DOM instead of HTML string manipulation --- plugins/missions.css | 119 +++++++++++++++++ plugins/missions.user.js | 279 +++++++++++++++++++++------------------ 2 files changed, 266 insertions(+), 132 deletions(-) create mode 100644 plugins/missions.css diff --git a/plugins/missions.css b/plugins/missions.css new file mode 100644 index 00000000..0e178c1e --- /dev/null +++ b/plugins/missions.css @@ -0,0 +1,119 @@ +.plugin-mission-summary { + padding: 5px; + border-top: black solid 1px; + min-height: 50px; + position: relative; + clear: left; +} +.plugin-mission-summary.checked::after { + content: "✓"; + display: block; + pointer-events: none; + position: absolute; + text-align: center; + color: rgba(255, 187, 0, 0.3); + left: 5px; + top: 5px; + font-size: 50px; + line-height: 50px; + width: 50px; +} +.plugin-mission-summary:first-child { + border-top-width: 0px; +} + +.plugin-mission-summary.checked { + background-color: rgba(255, 187, 0, 0.3); +} + +.plugin-mission-summary > img { + float: left; + cursor: pointer; + width: 50px; + margin-right: 10px; + margin-bottom: 10px; +} + +.plugin-mission-summary > a, .plugin-mission-summary > h4 { + display: block; + font-weight: bold; + font-size: 1.3em; + margin: 0 0 2px 60px; +} + +.plugin-mission-summary > br { + margin-bottom: 2px; +} + +.plugin-mission-info img { + height: 14px; + margin-right: 8px; + vertical-align: top; +} + +.plugin-mission-summary description { + white-space: pre; + margin-left: 110px; +} + +.plugin-mission-details .plugin-mission-summary.checked::after { + left: 0px; + top: 0px; + font-size: 100px; + line-height: 100px; + width: 100px; +} + +.plugin-mission-details .plugin-mission-summary { + padding: 0; + background-color: transparent; +} + +.plugin-mission-details .plugin-mission-summary > img { + width: 100px; +} + +.plugin-mission-details ol { + clear: left; + list-style: none; + margin: 0; + padding: 0; +} + +.plugin-mission-portal-indicator { + position: relative; + text-align: center; + float: left; + line-height: 18px; + height: 18px; + width: 18px; + margin-right: 5px; +} + +.plugin-mission-portal-indicator div { + border-color: currentcolor transparent transparent; + border-style: solid; + border-width: 2px 1px; + box-sizing: border-box; + height: 0; + left: 6px; + position: absolute; + top: 0; + transform-origin: 4px 9px 0; + width: 8px; +} + +.plugin-mission-waypoint .title { + font-size: 18px; + font-weight: bold; +} +.plugin-mission-waypoint label { + clear: left; + display: block; +} +.plugin-mission-waypoint input { + box-sizing: border-box; + margin: 3px 5px 8px 0; + width: 18px; +} + diff --git a/plugins/missions.user.js b/plugins/missions.user.js index 9e849359..48d69d18 100644 --- a/plugins/missions.user.js +++ b/plugins/missions.user.js @@ -249,168 +249,182 @@ window.plugin.missions = { }, renderMissionList: function(missions) { - return missions.map(this.renderMissionSummary, this).join(' '); + var container = document.createElement('div'); + missions.forEach(function(mission) { + container.appendChild(this.renderMissionSummary(mission)); + }, this); + return container; }, renderMissionSummary: function(mission) { var cachedMission = this.getMissionCache(mission.guid); - var html = ''; var checked = this.settings.checkedMissions[mission.guid]; - html += '
'; - html += ''; - html += '
' + mission.title + '
'; - if (cachedMission) { - html += '' + cachedMission.authorNickname + ''; - html += '
'; + var container = document.createElement('div'); + container.className = 'plugin-mission-summary mc-' + mission.guid; + if(checked) + container.classList.add('checked'); + + var img = container.appendChild(document.createElement('img')); + img.src = mission.image; + img.addEventListener('click', function(ev) { + plugin.missions.toggleMission(mission.guid); + }, false); + + var title = container.appendChild(document.createElement('a')); + title.textContent = mission.title; + title.href = '/mission/' + mission.guid; // TODO make IITC load on mission permalinks as well + title.addEventListener('click', function(ev) { + plugin.missions.openMission(mission.guid); + // prevent browser from following link + ev.preventDefault(); + return false; + }, false); + + if(cachedMission) { + var span = container.appendChild(document.createElement('span')); + span.className = 'nickname ' + (cachedMission.authorTeam === 'R' ? 'res' : 'enl') + span.textContent = cachedMission.authorNickname; } - html += '' + - timeToRemaining((mission.medianCompletionTimeMs / 1000) | 0); - html += '' + - (((mission.ratingE6 / 100) | 0) / 100) + '%'; - + + container.appendChild(document.createElement('br')); + + var infoTime = container.appendChild(document.createElement('span')); + infoTime.className = 'plugin-mission-info time'; + infoTime.textContent = timeToRemaining((mission.medianCompletionTimeMs / 1000) | 0) + ' '; + img = infoTime.insertBefore(document.createElement('img'), infoTime.firstChild); + img.src = 'https://commondatastorage.googleapis.com/ingress.com/img/tm_icons/time.png'; + + var infoRating = container.appendChild(document.createElement('span')); + infoRating.className = 'plugin-mission-info rating'; + infoRating.textContent = (((mission.ratingE6 / 100) | 0) / 100) + '%' + ' '; + img = infoRating.insertBefore(document.createElement('img'), infoRating.firstChild); + img.src = 'https://commondatastorage.googleapis.com/ingress.com/img/tm_icons/like.png'; + if (cachedMission) { - html += '' + - cachedMission.numUniqueCompletedPlayers; - html += '' + - cachedMission.waypoints.length; + var infoPlayers = container.appendChild(document.createElement('span')); + infoPlayers.className = 'plugin-mission-info players'; + infoPlayers.textContent = cachedMission.numUniqueCompletedPlayers + ' '; + img = infoPlayers.insertBefore(document.createElement('img'), infoPlayers.firstChild); + img.src = 'https://commondatastorage.googleapis.com/ingress.com/img/tm_icons/players.png'; + + var infoWaypoints = container.appendChild(document.createElement('span')); + infoWaypoints.className = 'plugin-mission-info waypoints'; + infoWaypoints.textContent = cachedMission.waypoints.length + ' '; + img = infoWaypoints.insertBefore(document.createElement('img'), infoWaypoints.firstChild); + img.src = 'https://commondatastorage.googleapis.com/ingress.com/img/map_icons/linkmodeicon.png'; } - html += '
'; - html += '
'; - return html; + + return container; }, renderMission: function(mission) { - var me = this; - var checked = this.settings.checkedMissions[mission.guid]; - //commondatastorage.googleapis.com/ingress.com/img/map_icons/linkmodeicon.png - var html = '
'; - html += ''; - html += ''; - html += ''; - html += ''; - html += '
' + mission.title + '
'; - html += '' + mission.authorNickname + ''; - html += '
'; - html += '
'; - html += '' + - timeToRemaining((mission.medianCompletionTimeMs / 1000) | 0); - html += '' + - (((mission.ratingE6 / 100) | 0) / 100) + '%'; - html += '' + - mission.numUniqueCompletedPlayers; - html += '' + - mission.waypoints.length; - html += '
'; - html += '

'; - html += mission.description; - html += '

'; - html += '
'; - html += mission.waypoints.map(function(waypoint, index) { - return me.renderMissionWaypoint(waypoint, index, mission); - }).join(' '); - return html; + var container = document.createElement('div'); + container.className = 'plugin-mission-details'; + + var summary = container.appendChild(this.renderMissionSummary(mission)); + + // replace link with heading + var title = summary.getElementsByTagName('a')[0]; + var newtitle = document.createElement('h4'); + newtitle.textContent = mission.title; + title.parentNode.replaceChild(newtitle, title); + + var desc = summary.appendChild(document.createElement('p')); + desc.className = 'description'; + desc.textContent = mission.description; + + + var list = container.appendChild(document.createElement('ol')) + mission.waypoints.forEach(function(waypoint, index) { + list.appendChild(this.renderMissionWaypoint(waypoint, index, mission)); + }, this); + + return container; }, renderMissionWaypoint: function(waypoint, index, mission) { - var html = ''; - html += '

'; - html += '

'; + var container = document.createElement('li'); + container.className = 'plugin-mission-waypoint'; + if (waypoint.portal) { - var color = 'white'; - if (waypoint.portal.team === 'R') { // Yay - color = '#00c2ff'; - } else if (waypoint.portal.team === 'E') { // Booo - color = '#28f428'; - } - var realHealth = (waypoint.portal.resCount / 8) * (waypoint.portal.health / 100); - html += this.renderPortalCircle(color, waypoint.portal.level, realHealth); - /* - var radius = ((realHealth * 360) | 0); - html += '
'; - html += '
'; - html += '
'; // 360 + 45 - html += '
' + waypoint.portal.level + '
'; - html += '
'; - */ - } - if (waypoint.portal) { - html += ''; - } - if (waypoint.title) { - html += waypoint.title; + container.appendChild(this.renderPortalCircle(waypoint.portal)); + + var title = container.appendChild(document.createElement('a')); + + var lat = waypoint.portal.latE6/1E6; + var lng = waypoint.portal.lngE6/1E6; + var perma = '/intel?ll='+lat+','+lng+'&z=17&pll='+lat+','+lng; + + title.href = perma; + title.addEventListener("click", function(ev) { + renderPortalDetails(waypoint.portal.guid); + ev.preventDefault(); + return false; + }, false); + title.addEventListener("dblclick", function(ev) { + zoomToAndShowPortal(waypoint.portal.guid, [lat, lng]); + ev.preventDefault(); + return false; + }, false); } else { - html += 'Unknown'; + var title = container.appendChild(document.createElement('span')); } - if (waypoint.portal) { - html += ''; - } - html += '
'; - /* - checkbox_grey - checkbox_orange - checkbox_cyan - */ - var img = 'cyan'; - if (index === 0) { - img = 'orange'; - } else if (!waypoint.objective) { - img = 'grey'; - } - // ✓ + + title.className = 'title'; + if(waypoint.title) + title.textContent = waypoint.title; + else if(waypoint.portal && waypoint.portal.title) + title.textContent = waypoint.portal.title; + else + title.textContent = 'Unknown'; + var mwpid = mission.guid + '-' + waypoint.guid; var checked = this.settings.checkedWaypoints[mwpid]; - - html += ''; - html += ''; - - html += ''; - html += ''; - html += (waypoint.objective ? waypoint.objective : '?'); - html += '

'; - return html; + + var label = container.appendChild(document.createElement('label')); + + var checkbox = label.appendChild(document.createElement('input')); + checkbox.type = 'checkbox'; + checkbox.addEventListener('change', function() { + plugin.missions.toggleWaypoint(mission.guid, waypoint.guid); + }, false); + checkbox.className = 'wp-' + mwpid; + + var objective = label.appendChild(document.createElement('span')); + objective.textContent = waypoint.objective ? waypoint.objective : '?'; + + return container; }, - renderPortalCircle: function(portalColor, portalLevel, portalHealth) { - var s = 20, - bg = '#999', - c = portalColor, - i = 14, - ic = '#555', - s2 = ((s / 2) | 0), - si = (((s - i) / 2) | 0), - d = (portalHealth * 180) | 0, - num = portalLevel; - var html = '
'; - html += '
'; - html += '
'; - html += '
'; - html += '
'; - html += '
'; - html += '
'; - html += '
'; - html += '
'; - html += '
'; - html += '
'; - html += '
'; - html += '
'; - html += '
' + num; - html += '
'; - html += '
'; - return html; + + renderPortalCircle: function(portal) { + var team = TEAM_TO_CSS[getTeam(portal)]; + var resCount = portal.resCount; + var level = resCount == 0 ? 0 : portal.level; // we want neutral portals to be level 0 + + var container = document.createElement('div'); + container.className = 'plugin-mission-portal-indicator help ' + team; + container.textContent = level; + container.title = 'Level:\t'+level+'\nResonators:\t'+resCount+'\nHealth:\t'+portal.health+'%'; + + for(var i = 0; i< resCount; i++) { + var resonator = container.appendChild(document.createElement('div')); + resonator.style.transform = 'rotate(' + i*45 + 'deg)'; + } + return container; }, toggleWaypoint: function(mid, wpid, dontsave) { var mwpid = mid + '-' + wpid; var el = document.getElementsByClassName('wp-' + mwpid); - if (!this.settings.checkedWaypoints[mwpid]) { + if(!this.settings.checkedWaypoints[mwpid]) { this.settings.checkedWaypoints[mwpid] = true; window.runHooks('waypointFinished', { mission: this.getMissionCache(mid), waypointguid: wpid }); - $(el).show(); } else { delete this.settings.checkedWaypoints[mwpid]; - $(el).hide(); } + $(el).prop('checked', !!this.settings.checkedWaypoints[mwpid]); if (!dontsave) { this.saveData(); } @@ -431,7 +445,7 @@ window.plugin.missions = { } }, this); $(el).show(); - $(sumel).css('background-color', 'rgba(255, 187, 0, 0.3)'); + $(sumel).addClass('checked'); window.runHooks('missionFinished', { mission: mission }); } else { delete this.settings.checkedMissions[mid]; @@ -441,7 +455,7 @@ window.plugin.missions = { } }, this); $(el).hide(); - $(sumel).css('background-color', ''); + $(sumel).removeClass('checked'); } this.saveData(); }, @@ -622,6 +636,7 @@ window.plugin.missions = { this.settings.checkedMissions = {}; } + $('