Skip to content

Instantly share code, notes, and snippets.

@kourge
Created September 13, 2010 17:22
Show Gist options
  • Save kourge/577667 to your computer and use it in GitHub Desktop.
Save kourge/577667 to your computer and use it in GitHub Desktop.
let self = require('self'), file = require('file'), url = require('url');
let contextMenu = require('context-menu');
let sym = gensym(), containerID = 'word-cloud-' + sym;
let item = contextMenu.Item({
label: "Show Word Cloud",
onClick: function(context, item) {
let win = context.window, corpus = generateCorpus(win);
let scores = score(corpus), sorted = plist(scores);
sorted.sort(byScore);
let scripts = ['jquery-1.4.2.js', 'jquery.masonry.js'];
if (!win.wrappedJSObject[sym]) {
loadScripts(win, scripts.map(self.data.url), function(win) {
setup(win, corpus, sorted);
});
} else {
arrange(win.wrappedJSObject[sym].jQuery, win, corpus, sorted);
}
},
context: function(context) {
return context.window.location != 'about:blank' &&
!context.document.getElementById(containerID);
}
});
contextMenu.add(item);
function loadScripts(win, scripts, callback) {
let length = scripts.length, loaded = 0, w = win.wrappedJSObject;
let head = w.document.getElementsByTagName('head')[0];
for (let i = 0; i < length; i++) {
let s = w.document.createElement('script');
s.addEventListener('load', function incr() {
loaded++;
if (loaded == length) callback(win);
}, false);
s.src = scripts[i];
head.appendChild(s);
}
}
function setup(win, corpus, sorted) {
let doc = win.document;
let $ = (function(win) {
win[sym] = {};
return (win[sym].jQuery = win.jQuery.noConflict(true));
})(win.wrappedJSObject);
$.fn.rotate = function rotate(deg) {
if (deg != 90 && deg != 270) throw new Error();
return this.each(function() {
let e = $(this.wrappedJSObject || this);
e.css({display: 'inline', cursor: 'vertical-text'});
let w = e.width(), h = e.height();
e.css({display: 'inline-block', MozTransformOrigin: 'left top'});
let style = {height: w + 'px', width: h + 'px'};
let t = ['rotate(' + deg + 'deg) translate', null, 'px)'];
t[1] = (deg == 90) ? 'Y(-' + h : 'X(-' + w;
style.MozTransform = t.join('');
e.css(style);
});
};
arrange($, win, corpus, sorted);
}
Math.randomIntBetween = function randomIntBetween(min, max) {
// jsocol's overkill randomInt
return Math.floor((Math.random()-0.5)*(max - min) + min + (max - min)/2);
};
let colors = [
'#b00', '#aff849', '#ff8000', '#ff0080', '#b0b', '#0080ff', '#ccc'
];
colors.randomItem = function randomItem() {
return this[Math.randomIntBetween(0, this.length - 1)];
};
function arrange($, win, corpus, sorted) {
let doc = win.wrappedJSObject.document;
let container = $('<div>', doc).css({
display: 'block', position: 'absolute',
top: 0, left: 0, right: 0, bottom: 0,
overflowX: 'hidden', overflowY: 'scroll', backgroundColor: 'black'
}).attr('id', containerID);
let close = $('<a>', doc).html('Close').attr('title', 'Close').css({
display: 'block', position: 'fixed', overflow: 'hidden', zIndex: 1,
width: '42px', height: 0, paddingTop: '42px', cursor: 'pointer',
backgroundImage: 'url(' + self.data.url('close-button.png') + ')'
}).click(function() {
$(doc.body).show();
container.remove();
});
container.append(close);
let stage = $('<div>', doc).css({
padding: '3em', width: '100%', height: '100%', zIndex: 0
});
container.append(stage);
$(doc.body.parentNode).append(container);
let parts = [[], []];
for (let i = 0, length = sorted.length; i < length; i++)
parts[i % 2].push(sorted[i]);
parts[0].reverse();
let weighted = parts[0].concat(parts[1]);
let bodyFont = $(doc.body).css('fontFamily');
weighted.forEach(function([word, score]) {
let e = $('<div>', doc).html(word).css({
display: 'inline-block', fontFamily: bodyFont,
color: colors.randomItem(), fontSize: (score * 1.5) + 'em'
}).addClass('word').hide();
stage.append(e);
let n = Math.randomIntBetween(0, 8);
(n == 1 || n == 3) && e.rotate(90);
(n == 2 || n == 4) && e.rotate(270);
e.show();
});
stage.masonry({itemSelector: '.word', resizeable: false});
$(doc.body).hide();
}
let expr = [
'//text()[normalize-space(.)]',
'[name(..)!="SCRIPT"][name(..)!="script"]',
'[name(..)!="STYLE"][name(..)!="style"]',
'[name(..)!="TEXTAREA"][name(..)!="textarea"]',
'[name(..)!="NOSCRIPT"][name(..)!="noscript"]'
].join('');
function generateCorpus(win) {
let results = [], query = win.document.evaluate(
expr, win.document, null,
win.wrappedJSObject.XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
);
for (let i = 0, length = query.snapshotLength; i < length; i++)
results.push(query.snapshotItem(i).textContent);
return results.join(' ');
}
let commonEnglishWords = [
'the', 'be', 'to', 'of', 'and', 'a', 'in', 'that', 'have', 'I', 'it',
'for', 'not', 'on', 'with', 'he', 'as', 'you', 'do', 'at', 'this',
'but', 'his', 'by', 'from', 'they', 'we', 'say', 'her', 'she', 'or',
'an', 'will', 'my', 'one', 'all', 'would', 'there', 'their', 'what',
'so', 'up', 'out', 'if', 'about', 'who', 'get', 'which', 'go', 'me',
'when', 'make', 'can', 'like', 'time', 'no', 'just', 'him', 'know',
'take', 'people', 'into', 'year', 'your', 'good', 'some', 'could',
'them', 'see', 'other', 'than', 'then', 'now', 'look', 'only', 'come',
'its', 'over', 'think', 'also', 'back', 'after', 'use', 'two', 'how',
'our', 'work', 'first', 'well', 'way', 'even', 'new', 'want', 'because',
'any', 'these', 'give', 'day', 'most', 'us', 'is', 'are'
];
function score(corpus) {
let words = {};
corpus.replace(/[A-Za-z_ ]+?(\s|$)/g, function(w) {
w = w.toLowerCase().trim();
if (w.length <= 3) return;
if (!words[w]) words[w] = 0;
words[w]++;
});
return words;
}
function plist(object) [[key, object[key]] for (key in object)];
function gensym() Math.round(Math.random() * 1e16).toString(36);
function byScore(a, b) b[1] - a[1];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment