// Wappalyzer by ElbertF 2009 http://elbertf.com var wappalyzer = {}; addEventListener('load', function() { wappalyzer.init(); }, false); addEventListener('unload', function() { wappalyzer.sendReport(); }, false); wappalyzer = { apps: {}, appsDetected: 0, autoDetect: true, browser: {}, currentTab: false, checkUnique: {}, enableTracking: true, history: {}, hitCount: 0, homeUrl: 'http://wappalyzer.com/', isBookmarklet: false, isMobile: false, newInstall: false, prevUrl: '', prefs: {}, regexDomain: /^[a-z0-9._\-]+\.[a-z]+/, req: false, request: false, showAppNames: 3, init: function() { wappalyzer.log('init'); wappalyzer.browser = typeof(Browser) != 'undefined' ? Browser.selectedBrowser : gBrowser; // Preferences wappalyzer.prefs = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefService).getBranch('wappalyzer.'); wappalyzer.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2); wappalyzer.prefs.addObserver('', wappalyzer, false); wappalyzer.showAppNames = wappalyzer.prefs.getIntPref( 'showAppNames'); wappalyzer.autoDetect = wappalyzer.prefs.getBoolPref('autoDetect'); wappalyzer.enableTracking = wappalyzer.prefs.getBoolPref('enableTracking'); wappalyzer.newInstall = wappalyzer.prefs.getBoolPref('newInstall'); // Open page after installation if ( wappalyzer.newInstall ) { wappalyzer.prefs.setBoolPref('newInstall', false); wappalyzer.browser.addEventListener('load', wappalyzer.installSuccess, false); } // Listen messages sent from the content process messageManager.addMessageListener('wappalyzer:onPageLoad', wappalyzer.onContentPageLoad); messageManager.loadFrameScript('chrome://wappalyzer/content/content.js', true); // Listen for URL changes wappalyzer.browser.addProgressListener(wappalyzer.urlChange, Components.interfaces.nsIWebProgress.NOTIFY_LOCATION); // Listen for page loads wappalyzer.browser.addEventListener('DOMContentLoaded', wappalyzer.onPageLoad, true); }, log: function(message) { return; var consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService); consoleService.logStringMessage("Wappalyzer: " + message); }, get strings() { return document.getElementById('wappalyzer-strings'); }, observe: function(subject, topic, data) { if ( topic != 'nsPref:changed' ) { return; } switch(data) { case 'autoDetect': wappalyzer.autoDetect = wappalyzer.prefs.getBoolPref('autoDetect'); break; case 'enableTracking': wappalyzer.enableTracking = wappalyzer.prefs.getBoolPref('enableTracking'); break; case 'showAppNames': wappalyzer.showAppNames = wappalyzer.prefs.getIntPref('showAppNames'); break; } }, onPageLoad: function(event) { wappalyzer.log('onPageLoad'); var doc = event.originalTarget; wappalyzer.analyzePage( doc.location.href, doc.documentElement.innerHTML, [], true, false ); }, onContentPageLoad: function(message) { wappalyzer.log('onContentPageLoad'); wappalyzer.analyzePage( message.json.href, message.json.html, message.json.headers, true, false ); }, onUrlChange: function(request) { wappalyzer.log('onUrlChange'); wappalyzer.clearDetectedApps(); var doc = wappalyzer.browser.contentDocument; if ( !doc.request ) { doc.request = request; } wappalyzer.request = doc.request; wappalyzer.analyzePage( doc.location.href, doc.documentElement ? doc.documentElement.innerHTML : '', [], false, false ); }, urlChange: { QueryInterface: function(iid) { if ( iid.equals(Components.interfaces.nsIWebProgressListener) || iid.equals(Components.interfaces.nsISupportsWeakReference) || iid.equals(Components.interfaces.nsISupports) ) { return this; } throw Components.results.NS_NOINTERFACE; }, onLocationChange: function(progress, request, url) { if ( !url ) { wappalyzer.prevUrl = ''; return; } if ( url.spec != wappalyzer.prevUrl ) { wappalyzer.prevUrl = url.spec; wappalyzer.onUrlChange(request); } }, onStateChange: function(a, b, c, d) {}, onProgressChange: function(a, b, c, d, e, f) {}, onStatusChange: function(a, b, c, d) {}, onSecurityChange: function(a, b, c) {} }, analyzePage: function(href, html, headers, doCount, manualDetect) { wappalyzer.log('analyzePage'); wappalyzer.currentTab = false; if ( !wappalyzer.isBookmarklet ) { if ( href == wappalyzer.browser.contentDocument.location.href ) { wappalyzer.currentTab = true; } } if ( typeof(html) == 'undefined' ) { html = ''; } if ( wappalyzer.autoDetect || ( !wappalyzer.autoDetect && manualDetect ) ) { // Scan URL, domain and response headers for patterns if ( html.length > 50000 ) // Prevent large documents from slowing things down { html = html.substring(0, 25000) + html.substring(html.length - 25000, html.length); } if ( html ) { for ( var appName in wappalyzer.apps ) { if ( typeof(wappalyzer.checkUnique[appName]) == 'undefined' ) // Don't scan for apps that have already been detected { // Scan HTML if ( typeof(wappalyzer.apps[appName].html) != 'undefined' ) { var regex = wappalyzer.apps[appName].html; if ( regex.test(html) ) { wappalyzer.showApp(appName, href, doCount); } } // Scan URL if ( href && typeof(wappalyzer.apps[appName].url) != 'undefined' ) { var regex = wappalyzer.apps[appName].url; if ( regex.test(href) ) { wappalyzer.showApp(appName, href, doCount); } } // Scan response header if ( typeof(wappalyzer.apps[appName].headers) != 'undefined' && wappalyzer.request ) { for ( var header in wappalyzer.apps[appName].headers ) { var regex = wappalyzer.apps[appName].headers[header]; try { if ( regex.test(wappalyzer.request.nsIHttpChannel.getResponseHeader(header)) ) { wappalyzer.showApp(appName, href, doCount); } } catch(e) { } } } } } } html = ''; // Free memory } }, showApp: function(detectedApp, href, doCount) { wappalyzer.log('showApp ' + detectedApp); if ( !wappalyzer.currentTab && !wappalyzer.isBookmarklet ) { wappalyzer.report(detectedApp, href); return; } if ( detectedApp && typeof(wappalyzer.checkUnique[detectedApp]) == 'undefined' ) { switch ( true ) { case wappalyzer.isBookmarklet: var e = document.getElementById('wappalyzer-bookmarklet-apps'); e.innerHTML = ( wappalyzer.appsDetected ? e.innerHTML : '' ) + '<a href="' + wappalyzer.homeUrl + 'stats/app/' + escape(wappalyzer.app[i]) + '" style="color: #332;">' + wappalyzer.app[i] + '</a><br/>' ; break; case wappalyzer.isMobile: default: // Hide Wappalyzer icon document.getElementById('wappalyzer-icon').style.display = 'none'; // Show app icon and label var e = document.getElementById('wappalyzer-detected-apps'); var child = document.createElement('image'); child.setAttribute('src', 'chrome://wappalyzer/skin/app_icons/' + detectedApp + '.ico'); child.setAttribute('class', 'wappalyzer-icon'); if ( !wappalyzer.isMobile ) { if ( wappalyzer.showAppNames == 2 ) { var urlbar = document.getElementById('wappalyzer-urlbar'); var tooltiptext = urlbar.getAttribute('tooltiptext') + '\n' + detectedApp; urlbar.setAttribute('tooltiptext', tooltiptext); } if ( wappalyzer.showAppNames == 3 ) { child.setAttribute('onmouseover', 'wappalyzer.showLabels(true)'); child.setAttribute('onmouseout', 'wappalyzer.showLabels(false)'); } if ( wappalyzer.appsDetected ) { child.setAttribute('style', 'margin-left: .5em'); } } e.appendChild(child); if ( !wappalyzer.isMobile ) { child = document.createElement('label'); child.setAttribute('value', detectedApp); child.setAttribute('class', 'wappalyzer-app-name'); if ( wappalyzer.showAppNames != 1 ) { child.setAttribute('style', 'display: none;'); } if ( wappalyzer.showAppNames == 3 ) { child.setAttribute('onmouseover', 'wappalyzer.showLabels(true)'); child.setAttribute('onmouseout', 'wappalyzer.showLabels(false)'); } e.appendChild(child); // Enable application statistics menu item var e = document.getElementById('wappalyzer-app-stats'); e.parentNode.setAttribute('disabled', false); var child = document.createElement('menuitem'); child.setAttribute('label', detectedApp); child.setAttribute('class', 'menuitem-iconic'); child.setAttribute('type', ''); child.setAttribute('image', 'chrome://wappalyzer/skin/app_icons/' + detectedApp + '.ico'); child.setAttribute('oncommand', 'wappalyzer.openTab(\'' + wappalyzer.homeUrl + 'stats/app/' + escape(detectedApp) + '\');'); e.appendChild(child); } } if ( doCount ) { wappalyzer.report(detectedApp, href); } wappalyzer.appsDetected ++; wappalyzer.checkUnique[detectedApp] = true; } }, report: function(detectedApp, href) { wappalyzer.log('report'); domain = href.match(/:\/\/(.[^/]+)/)[1]; if ( wappalyzer.enableTracking && wappalyzer.regexDomain.test(domain) ) { if ( typeof(wappalyzer.history[domain]) == 'undefined' ) { wappalyzer.history[domain] = []; } if ( typeof(wappalyzer.history[domain][detectedApp]) == 'undefined' ) { wappalyzer.history[domain][detectedApp] = 0; } wappalyzer.history[domain][detectedApp] ++; wappalyzer.hitCount ++; if ( wappalyzer.hitCount > 100 ) { wappalyzer.sendReport(); } } }, sendReport: function() // Anonymously send the name of the detected apps and domains to wappalyzer.com // You can turn this off in the options dialog // This is used to track the distibution of software, stats are publically available on the site { wappalyzer.log('sendReport'); if ( wappalyzer.enableTracking && !wappalyzer.req ) { var report = ''; var i, j; if ( wappalyzer.history ) { for ( i in wappalyzer.history ) { report += '[' + i; for ( j in wappalyzer.history[i] ) { report += '|' + j + ':' + wappalyzer.history[i][j]; } report += ']'; } } // Make POST request wappalyzer.req = new XMLHttpRequest(); wappalyzer.req.open('POST', wappalyzer.homeUrl + 'report/', true); wappalyzer.req.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE; wappalyzer.req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); wappalyzer.req.onreadystatechange = function(e) { if ( wappalyzer.req.readyState == 4 ) { if ( wappalyzer.req.status == 200 ) { // Reset report = ''; wappalyzer.hitCount = 0; wappalyzer.history = []; } wappalyzer.req.close(); wappalyzer.req = false; } }; wappalyzer.req.send('d=' + encodeURIComponent(report)); } }, clearDetectedApps: function() { wappalyzer.log('clearDetectedApps'); wappalyzer.appsDetected = 0; wappalyzer.checkUnique = []; // Show Wappalyzer icon document.getElementById('wappalyzer-icon').style.display = ''; // Clear app icons and labels e = document.getElementById('wappalyzer-detected-apps'); while ( e.childNodes.length > 0 ) { e.removeChild(e.childNodes.item(0)); } if ( !wappalyzer.isMobile ) { // Clear tooltip var urlbar = document.getElementById('wappalyzer-urlbar'); urlbar.setAttribute('tooltiptext', wappalyzer.strings.getString('wappalyzer.title') + '\n---'); // Disable and clear application statistics menu item e = document.getElementById('wappalyzer-app-stats'); e.parentNode.setAttribute('disabled', true); while ( e.childNodes.length > 0 ) { e.removeChild(e.childNodes.item(0)); } } }, showLabels: function(show) { e = document.getElementsByClassName('wappalyzer-app-name'); for ( i = 0; i < e.length; i ++ ) { e[i].style.display = show ? 'inline' : 'none'; } }, installSuccess: function() { wappalyzer.browser.removeEventListener('load', wappalyzer.installSuccess, false); wappalyzer.openTab(wappalyzer.homeUrl + 'install/success/'); }, openTab: function(url) { wappalyzer.browser.selectedTab = wappalyzer.browser.addTab(url); }, bookmarklet: function() { if ( typeof(gBrowser) == 'undefined' && typeof(Browser) == 'undefined' ) { wappalyzer.isBookmarklet = true; if ( !document.getElementById('wappalyzer-bookmarklet') ) { var body = document.getElementsByTagName('body')[0]; if ( body ) { var container = document.createElement('div'); container.innerHTML = '<div id="wappalyzer-bookmarklet" style="' + ' color: #332;' + ' font: 12px \'Trebuchet MS\';' + ' position: fixed;' + ' text-align: right;' + ' right: 2em;' + ' top: 2em;' + ' z-index: 9999999999;' + ' ">' + ' <div id="wappalyzer-container" style="' + ' -moz-border-radius: 7px;' + ' -webkit-border-radius: 7px;' + ' background: #FAFAFA;' + ' border: 7px solid #332;' + ' margin-bottom: .3em;' + ' min-width: 15em;' + ' padding: 1em 2em;' + ' text-align: center;' + ' ">' + ' <div style="' + ' border-bottom: 1px solid #332;' + ' font-size: 13px;' + ' padding-bottom: 1em;' + ' margin-bottom: 1em;' + ' "><strong>Wappalyzer</strong></div>' + ' <span id="wappalyzer-bookmarklet-apps"><em>No apps detected</em></span>' + ' </div>' + ' <span style="float: left;"><a href="http://wappalyzer.com" style="color: #332 !important;">home</a> | <a href="http://twitter.com/ElbertF" style="color: #332 !important;">follow me</a></span>' + ' <span style="float: right;">click to close</span>' + '</div>' ; container.onclick = function() { body.removeChild(container); }; body.appendChild(container); wappalyzer.analyzePage( document.location.href, document.documentElement.innerHTML, [], false ); } } } } }; wappalyzer.bookmarklet();