You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

485 lines
11 KiB

(function() {
'use strict';
var
w = require('wappalyzer').wappalyzer,
{Cc, Ci} = require('chrome'),
headersCache = {},
categoryNames = {},
windows = [],
Window,
Tab,
Panel,
Widget,
widget, // Keep track of a global widget instance. A widget per window does not work.
UrlBar;
exports.main = function(options, callbacks) {
w.log('main: ' + options.loadReason);
w.init();
};
exports.onUnload = function(reason) {
var win;
w.log('unload: ' + reason);
for each ( win in windows ) {
win.destroy();
}
};
Window = function(win) {
var
self = this,
tab;
w.log('new Window');
this.window = win;
this.tabs = {};
this.urlBar = null;
this.widget = null;
if ( require('sdk/simple-prefs').prefs.urlbar ) {
this.urlBar = new UrlBar(this.window);
} else {
this.widget = widget || ( widget = new Widget() );
}
require('sdk/simple-prefs').on('urlbar', function() {
self.destroy();
if ( require('sdk/simple-prefs').prefs.urlbar ) {
self.urlBar = new UrlBar(this.window);
} else {
self.widget = widget || ( widget = new Widget() );
}
self.displayApps();
});
for each ( tab in this.window.tabs ) {
this.tabs[tab.id] = new Tab(tab);
}
this.window.tabs
.on('open', function(tab) {
self.tabs[tab.id] = new Tab(tab);
})
.on('close', function(tab) {
self.tabs[tab.id] = null;
})
.on('activate', function(tab) {
self.displayApps();
});
self.displayApps();
};
Window.prototype.displayApps = function() {
var
self = this,
tab = this.window.tabs.activeTab,
url,
count = 0,
message = {};
w.log('Window.displayApps');
if ( !tab || require('sdk/tabs').activeTab !== tab ) {
return;
}
url = tab.url.replace(/#.*$/, '');
count = w.detected[url] ? Object.keys(w.detected[url]).length : 0;
this.tabs[tab.id].count = count;
this.tabs[tab.id].appsDetected = w.detected[url];
message = {
tabs: this.tabs[tab.id],
apps: w.apps,
categories: w.categories,
categoryNames: categoryNames
};
if ( this.urlBar ) {
this.urlBar.clear();
// Add icons
if ( count ) {
for ( appName in this.tabs[tab.id].appsDetected ) {
this.urlBar.addIcon(appName);
}
} else {
this.urlBar.addIcon();
}
this.urlBar.panel.get().port.emit('displayApps', message);
}
if ( this.widget ) {
this.widget.setIcon();
if ( count ) {
var
appName,
found = false;
// Find the main application to display
w.driver.categoryOrder.forEach(function(match) {
for ( appName in w.detected[url] ) {
w.apps[appName].cats.forEach(function(cat) {
if ( cat == match && !found ) {
self.widget.setIcon(appName);
found = true;
}
});
}
});
}
this.widget.panel.get().port.emit('displayApps', message);
}
};
Window.prototype.destroy = function() {
if ( this.urlBar ) {
this.urlBar.destroy();
this.urlBar = null;
}
if ( this.widget ) {
this.widget.destroy();
this.widget = null;
}
};
Tab = function(tab) {
tab.on('ready', function(tab) {
var worker = tab.attach({
contentScriptFile: require('sdk/self').data.url('js/tab.js')
});
worker.port.on('analyze', function(message) {
var url = message.url.replace(/#.*$/, '');
if ( typeof headersCache[url] !== 'undefined' ) {
message.analyze.headers = headersCache[url];
}
w.analyze(message.hostname, url, message.analyze);
});
worker.port.on('log', function(message) {
w.log('[ tab.js ] ' + message);
});
});
};
Panel = function() {
var self = this;
this.panel = require('sdk/panel').Panel({
width: 250,
height: 50,
contentURL: require('sdk/self').data.url('panel.html'),
contentScriptFile: require('sdk/self').data.url('js/panel.js'),
position: { right: 30, top: 30 }
});
this.panel.port.on('resize', function(height) {
self.panel.height = height;
});
this.panel.port.on('goToUrl', function(url) {
self.panel.hide();
w.driver.goToURL({ url: w.config.websiteURL + url, medium: 'panel' });
});
};
Panel.prototype.get = function() {
return this.panel;
};
Panel.prototype.destroy = function() {
this.panel.destroy();
};
Widget = function() {
this.panel = new Panel();
this.widget = require('sdk/widget').Widget({
id: 'wappalyzer',
label: 'Wappalyzer',
contentURL: require('sdk/self').data.url('images/icon32.png'),
panel: this.panel.get()
});
};
Widget.prototype.setIcon = function(appName) {
var url = typeof appName === 'undefined' ? 'images/icon32_hot.png' : 'images/icons/' + appName + '.png';
this.get().contentURL = require('sdk/self').data.url(url);
};
Widget.prototype.get = function() {
return this.widget;
};
Widget.prototype.destroy = function() {
this.panel.destroy();
this.widget.destroy();
};
UrlBar = function(window) {
var self = this;
this.panel = new Panel();
this.onClick = function() {
self.panel.get().show();
}
// Can't get document from sdk/windows. Use active window instead.
// This breaks switching between URL bar and widget with multiple windows open
this.document = Cc['@mozilla.org/appshell/window-mediator;1'].getService(Ci.nsIWindowMediator)
.getMostRecentWindow('navigator:browser').document;
if ( this.document.getElementById('wappalyzer-urlbar') ) {
this.urlBar = this.document.getElementById('wappalyzer-urlbar');
return;
}
//
this.urlBar = this.document.createElement('hbox');
this.urlBar.setAttribute('id', 'wappalyzer-urlbar');
this.urlBar.setAttribute('style', 'cursor: pointer; margin: 0 2px;');
this.urlBar.setAttribute('tooltiptext', require('sdk/l10n').get('name'));
this.urlBar.addEventListener('click', this.onClick);
this.document.getElementById('urlbar-icons').appendChild(this.urlBar);
};
UrlBar.prototype.get = function() {
return this.urlBar;
};
UrlBar.prototype.addIcon = function(appName) {
var
icon = this.document.createElement('image'),
url = typeof appName === 'undefined' ? 'images/icon32.png' : 'images/icons/' + appName + '.png',
tooltipText = ( typeof appName !== 'undefined' ? appName + ' - ' + require('sdk/l10n').get('clickForDetails') + ' - ' : '' ) + require('sdk/l10n').get('name');
icon.setAttribute('src', require('sdk/self').data.url(url));
icon.setAttribute('class', 'wappalyzer-icon');
icon.setAttribute('width', '16');
icon.setAttribute('height', '16');
icon.setAttribute('style', 'margin: 0 1px;');
icon.setAttribute('tooltiptext', tooltipText);
this.get().appendChild(icon);
return this;
};
UrlBar.prototype.clear = function() {
var icons;
do {
icons = this.get().getElementsByClassName('wappalyzer-icon');
if ( icons.length ) {
this.get().removeChild(icons[0]);
}
} while ( icons.length );
return this;
};
UrlBar.prototype.destroy = function() {
this.panel.destroy();
this.urlBar.removeEventListener('click', this.onClick);
this.urlBar.remove();
return this;
}
w.driver = {
/**
* Log messages to console
*/
log: function(args) {
console.log(args.message);
},
/**
* Initialize
*/
init: function(callback) {
var
id,
version,
win,
httpRequestObserver,
json = JSON.parse(require('sdk/self').data.load('apps.json'));
w.log('driver.init');
try {
version = require('sdk/self').version;
if ( !require('sdk/simple-storage').storage.version ) {
w.driver.goToURL({ url: w.config.websiteURL + 'installed', medium: 'install' });
} else if ( version !== require('sdk/simple-storage').storage.version ) {
w.driver.goToURL({ url: w.config.websiteURL + 'upgraded', medium: 'upgrade', background: true });
}
require('sdk/simple-storage').storage.version = version;
} catch(e) { }
w.apps = json.apps;
w.categories = json.categories;
for ( id in w.categories ) {
categoryNames[id] = require('sdk/l10n').get('cat' + id);
}
require('sdk/windows').browserWindows
.on('open', function(win) {
windows.push(new Window(win));
});
for each ( win in require('sdk/windows').browserWindows ) {
windows.push(new Window(win));
}
httpRequestObserver = {
init: function() {
var observerService = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService);
observerService.addObserver(this, 'http-on-examine-response', false);
},
observe: function(subject, topic, data) {
if ( topic == 'http-on-examine-response' ) {
subject.QueryInterface(Ci.nsIHttpChannel);
this.onExamineResponse(subject);
}
},
onExamineResponse: function (subject) {
var uri = subject.URI.spec.replace(/#.*$/, ''); // Remove hash
if ( headersCache.length > 50 ) {
headersCache = {};
}
if ( subject.contentType === 'text/html' ) {
if ( typeof headersCache[uri] === 'undefined' ) {
headersCache[uri] = {};
}
subject.visitResponseHeaders(function(header, value) {
headersCache[uri][header.toLowerCase()] = value;
});
}
}
};
httpRequestObserver.init();
},
goToURL: function(args) {
var url = args.url + ( typeof args.medium === 'undefined' ? '' : '?pk_campaign=firefox&pk_kwd=' + args.medium);
require('sdk/tabs').open({ url: url, inBackground: typeof args.background !== 'undefined' && args.background });
},
ping: function() {
var Request = require('sdk/request').Request;
if ( Object.keys(w.ping.hostnames).length && require('sdk/simple-prefs').prefs.tracking ) {
Request({
url: w.config.websiteURL + 'ping/v2/',
content: { json: encodeURIComponent(JSON.stringify(w.ping)) },
onComplete: function (response) {
w.log('w.driver.ping: status ' + response.status);
}
}).post();
w.log('w.driver.ping: ' + JSON.stringify(w.ping));
w.ping = { hostnames: {} };
}
},
displayApps: function() {
var win;
for each ( win in windows ) {
win.displayApps();
}
},
categoryOrder: [ // Used to pick the main application
1, // CMS
11, // Blog
6, // Web Shop
2, // Message Board
8, // Wiki
13, // Issue Tracker
30, // Web Mail
18, // Web Framework
21, // LMS
7, // Photo Gallery
38, // Media Server
3, // Database Manager
34, // Database
4, // Documentation Tool
9, // Hosting Panel
29, // Search Engine
12, // Javascript Framework
26, // Mobile Framework
25, // Javascript Graphics
22, // Web Server
27, // Programming Language
28, // Operating System
15, // Comment System
20, // Editor
41, // Payment Processor
10, // Analytics
32, // Marketing Automation
31, // CDN
23, // Cache Tool
17, // Font Script
24, // Rich Text Editor
35, // Map
5, // Widget
14, // Video Player
16, // Captcha
33, // Web Server Extension
37, // Network Device
39, // Webcam
40, // Printer
36, // Advertising Network
42, // Tag Managers
43, // Paywalls
19 // Miscellaneous
]
};
}());