-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
169 lines (127 loc) · 4.17 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/**
* Module dependencies.
*/
var bind = require('bind')
, each = require('each')
, Emitter = require('emitter')
, on = require('event').bind
, query = require('query')
, uid = require('uid');
/**
* The HN object is really just an emitter for listening to button events.
*/
var HN = new Emitter();
/**
* When an iframe first loads it send along its width, so we can resize the
* <iframe> in the DOM. This way it never takes up more space than it actually
* needs, so multiple button in a row are next to each other.
*/
HN.on('load', function (event) {
var iframe = event.target;
iframe.width = Math.ceil(event.width); // browsers round weirdly, ceil helps
});
/**
* Initialize a button element.
*/
HN.initialize = function (a) {
new Button(a);
};
/**
* Initialize a button. Generate a unique ID that we can use when messaging
* between windows to identify the sender.
*
* @param {Element} button The button's element in the DOM.
*/
function Button (a) {
var host = a.getAttribute('data-host') || 'hn-button.herokuapp.com';
this.origin = location.protocol + '//' + host;
this.id = 'hn-button-' + uid();
on(window, 'message', bind(this, this.onMessage));
this.render(a);
}
/**
* Render a button.
*
* @param {Element} a The original <a> element that was on the page.
*/
Button.prototype.render = function (a) {
// Grab some settings from the <a>.
var options = {
title: a.getAttribute('data-title') || document.title,
url: a.getAttribute('data-url') || window.location.href,
style: a.getAttribute('data-style'),
count: a.getAttribute('data-count')
};
// Create the iframe element that we will replace the <a> with.
var iframe = this.iframe = document.createElement('iframe');
// Set the source based on data attributes, with fallbacks.
iframe.src = this.origin + stringify(options);
// Add the id, name, class, and I think it's nice to see the same attributes
// you set on the <a> stay on the iframe.
iframe.id = iframe.name = this.id;
iframe.className = 'hn-button';
iframe.setAttribute('data-title', options.title);
iframe.setAttribute('data-url', options.url);
if (options.style) iframe.setAttribute('data-style', options.style);
if (options.count) iframe.setAttribute('data-count', options.count);
// Give it a title for accessibility.
iframe.title = 'Hacker News Button';
// Set the proper width and height, depending on the orientation.
iframe.height = options.count === 'vertical' ? 62 : 20; // standard
iframe.width = 100; // a best guess, real width applied on load
// Set other required attributes.
iframe.frameBorder = 0; // removes default iframe border
// Replace the <a> with the iframe.
a.parentNode.insertBefore(iframe, a);
a.parentNode.removeChild(a);
};
/**
* Listen for messages coming from our iframe's window and proxy them to the
* global HN object so others can react.
*
* @param {MessageEvent} message The message from the postMessage API.
*/
Button.prototype.onMessage = function (message) {
// make sure we're listening for the right thing
if (message.origin !== this.origin) return;
if (message.data.id !== this.id) return;
var event = message.data.event
, data = message.data.data;
// add properties so the listener can differentiate
data.type = event;
data.id = this.id;
data.target = this.iframe;
// emit on the global HN object
HN.emit(event, data);
};
/**
* Stringify a querystring from an options dictionary.
*
* @param {Object} options The options to use.
* @return {String} The iframe `src` href.
*/
function stringify (options) {
var query = '';
each(options, function (key, value) {
if ('host' == key) return;
query += query ? '&' : '?';
if (value) query += key + '=' + encodeURIComponent(value);
});
return query;
}
/**
* Kick everything off, initializing all the `.hn-button`'s on the page.
*/
each(query.all('.hn-button'), HN.initialize);
/**
* Replay existing queued messages into the real HN object.
*/
if (window.HN) while (window.HN.length > 0) {
var item = window.HN.shift();
var method = item.shift();
if (HN[method]) HN[method].apply(HN, item);
}
/**
* Module exports.
*/
module.exports = HN;