forked from jeffomatic/nojquery-postmessage
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnojquery.ba-postmessage.js
145 lines (125 loc) · 4.63 KB
/
nojquery.ba-postmessage.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
/*!
* jQuery postMessage - v0.5 - 9/11/2009
* http://benalman.com/projects/jquery-postmessage-plugin/
*
* Copyright (c) 2009 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*
* Non-jQuery fork by Jeff Lee
*
* This fork consists of the following changes:
* 1. Basic code cleanup and restructuring, for legibility.
* 2. The `postMessage` and `receiveMessage` functions can be bound arbitrarily,
* in terms of both function names and object scope. Scope is specified by
* the the "this" context of NoJQueryPostMessageMixin();
* 3. I've removed the check for Opera 9.64, which used `$.browser`. There were
* at least three different GitHub users requesting the removal of this
* "Opera sniff" on the original project's Issues page, so I figured this
* would be a relatively safe change.
* 4. `postMessage` no longer uses `$.param` to serialize messages that are not
* strings. I actually prefer this structure anyway. `receiveMessage` does
* not implement a corresponding deserialization step, and as such it seems
* cleaner and more symmetric to leave both data serialization and
* deserialization to the client.
* 5. The use of `$.isFunction` is replaced by a functionally-identical check.
* 6. The `$:nomunge` YUI option is no longer necessary.
*/
function NoJQueryPostMessageMixin(postBinding, receiveBinding) {
var eventFunctions, eventMessage, currentMsgCallback,
intervalId, lastHash, cacheBust = 1;
if (window.postMessage) {
if (window.addEventListener) {
eventFunctions = {
add: 'addEventListener',
remove: 'removeEventListener'
}
eventMessage = 'message';
} else {
eventFunctions = {
add: 'attachEvent',
remove: 'detachEvent'
}
eventMessage = 'onmessage';
}
function setMessageCallback(callback) {
window[eventFunctions.add](eventMessage, callback);
return callback;
}
function unsetMessageCallback(callback) {
window[eventFunctions.remove](eventMessage, callback);
}
this[postBinding] = function(message, targetUrl, target) {
if (!targetUrl) {
return;
}
// The browser supports window.postMessage, so call it with a targetOrigin
// set appropriately, based on the targetUrl parameter.
target.postMessage( message, targetUrl.replace( /([^:]+:\/\/[^\/]+).*/, '$1' ) );
}
// Since the browser supports window.postMessage, the callback will be
// bound to the actual event associated with window.postMessage.
this[receiveBinding] = function(callback, sourceOrigin, delay) {
// Unbind an existing callback if it exists.
if (currentMsgCallback) {
unsetMessageCallback(currentMsgCallback);
currentMsgCallback = null;
}
if (!callback) {
return false;
}
// Bind the callback. A reference to the callback is stored for ease of
// unbinding.
currentMsgCallback = setMessageCallback(function(e) {
switch(Object.prototype.toString.call(sourceOrigin)) {
case '[object String]':
if (sourceOrigin !== e.origin) {
return false;
}
break;
case '[object Function]':
if (sourceOrigin(e.origin)) {
return false;
}
break;
}
callback(e);
});
};
} else {
this[postBinding] = function(message, targetUrl, target) {
if (!targetUrl) {
return;
}
// The browser does not support window.postMessage, so set the location
// of the target to targetUrl#message. A bit ugly, but it works! A cache
// bust parameter is added to ensure that repeat messages trigger the
// callback.
target.location = targetUrl.replace( /#.*$/, '' ) + '#' + (+new Date) + (cacheBust++) + '&' + message;
}
// Since the browser sucks, a polling loop will be started, and the
// callback will be called whenever the location.hash changes.
this[receiveBinding] = function(callback, sourceOrigin, delay) {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
if (callback) {
delay = typeof sourceOrigin === 'number'
? sourceOrigin
: typeof delay === 'number'
? delay
: 100;
intervalId = setInterval(function(){
var hash = document.location.hash,
re = /^#?\d+&/;
if ( hash !== lastHash && re.test( hash ) ) {
lastHash = hash;
callback({ data: hash.replace( re, '' ) });
}
}, delay );
}
};
}
return this;
}