-
-
Save whatwewant/ebc99f06a7f97aaad0550c7b7cbcb9a6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*jslint browser: true */ | |
/*global jQuery */ | |
var Bridge = (function (window, jQuery) { | |
"use strict"; | |
var Bridge; | |
Bridge = { | |
callbackId: 0, | |
callbacks: [] | |
}; | |
function postMessage(targetWindow, object, targetOrigin) { | |
if (targetOrigin === 'null') { | |
targetOrigin = '*'; | |
} | |
targetWindow.postMessage(JSON.stringify(object), targetOrigin); | |
} | |
function Plugin() { | |
this.events = {}; | |
} | |
Plugin.prototype = { | |
callbackContext: null, | |
events: null, | |
addEventListener: function (type) { | |
if (this.events[type]) { | |
this.events[type].addEventListener(this.callbackContext); | |
} else { | |
this.callbackContext.error('Bridge.INVALID_EVENT'); | |
} | |
}, | |
removeEventListener: function (type, callbackId) { | |
if (this.events[type]) { | |
this.events[type].removeEventListener(type, callbackId); | |
} else { | |
this.callbackContext.error('Bridge.INVALID_EVENT'); | |
} | |
}, | |
execute: function (action, args, callbackContext) { | |
var result; | |
this.callbackContext = callbackContext; | |
if (jQuery.isFunction(this[action]) && action !== 'execute') { | |
result = this[action].apply(this, args); | |
} else { | |
callbackContext.error('Bridge.INVALID_ACTION'); | |
} | |
this.callbackContext = null; | |
return result; | |
} | |
}; | |
function PluginResult(status, message) { | |
this.status = status; | |
this.message = message; | |
} | |
PluginResult.Status = { | |
NO_RESULT: 0, | |
OK: 1, | |
ERROR: 2 | |
}; | |
PluginResult.prototype = { | |
status: 0, | |
message: undefined, | |
keepCallback: false, | |
setKeepCallback: function (b) { | |
this.keepCallback = b; | |
}, | |
getStatus: function () { | |
return this.status; | |
}, | |
getKeepCallback: function () { | |
return this.keepCallback; | |
} | |
}; | |
function PluginEvent() { | |
this.callbackContexts = []; | |
} | |
PluginEvent.prototype = { | |
counter: 0, | |
callbackContexts: null, | |
addEventListener: function (callbackContext) { | |
this.callbackContexts[callbackContext.getCallbackId()] = callbackContext; | |
++this.counter; | |
if (this.counter === 1) { | |
this.start(); | |
} | |
}, | |
removeEventListener: function (callbackId) { | |
if (this.callbackContexts[callbackId]) { | |
this.callbackContexts[callbackId].sendPluginResult(new PluginResult(PluginResult.Status.NO_RESULT)); | |
delete this.callbackContexts[callbackId]; | |
--this.counter; | |
if (!this.counter) { | |
this.stop(); | |
} | |
} | |
}, | |
start: function () {}, | |
stop: function () {}, | |
fireEvent: function (message) { | |
var result, callbackId, | |
callbackContexts = this.callbackContexts; | |
result = new PluginResult(PluginResult.Status.OK, message); | |
result.setKeepCallback(true); | |
for (callbackId in callbackContexts) { | |
if (callbackContexts.hasOwnProperty(callbackId)) { | |
callbackContexts[callbackId].sendPluginResult(result); | |
} | |
} | |
} | |
}; | |
function addEvent(plugin, type, prototype) { | |
var eventClass = function () {}; | |
eventClass.prototype = jQuery.extend(new PluginEvent(), prototype); | |
plugin.events[type] = new eventClass(); // jshint ignore: line | |
} | |
function CallbackContext(callbackId, targetWindow, targetOrigin) { | |
this.callbackId = callbackId; | |
this.targetWindow = targetWindow; | |
this.targetOrigin = targetOrigin; | |
} | |
CallbackContext.prototype = { | |
callbackId: '', | |
targetWindow: null, | |
targetOrigin: '', | |
finished: false, | |
isFinished: function () { | |
return this.finished; | |
}, | |
getCallbackId: function () { | |
return this.callbackId; | |
}, | |
sendPluginResult: function (pluginResult) { | |
if (this.finished) { | |
return; | |
} | |
this.finished = !pluginResult.getKeepCallback(); | |
var object = { | |
callbackId: this.callbackId, | |
keepCallback: !this.finished, | |
status: pluginResult.status, | |
message: pluginResult.message | |
}; | |
postMessage(this.targetWindow, object, this.targetOrigin); | |
}, | |
success: function (message) { | |
this.sendPluginResult(new PluginResult(PluginResult.Status.OK, message)); | |
}, | |
error: function (message) { | |
this.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message)); | |
} | |
}; | |
function exec(targetWindow, targetOrigin, success, fail, service, action, args) { | |
var callbackId = service + Bridge.callbackId++; | |
if (success || fail) { | |
Bridge.callbacks[callbackId] = {success: success, fail: fail}; | |
} | |
postMessage(targetWindow, {callbackId: callbackId, service: service, action: action, args: Array.prototype.slice.call(args, 0)}, targetOrigin); | |
return callbackId; | |
} | |
jQuery(window).on('message', function (jqEvent) { | |
var event, object, callbackContext, service, result, callbackId, callback; | |
event = jqEvent.originalEvent; | |
try { | |
object = jQuery.parseJSON(event.data); | |
} catch (exception) { | |
console.error('Bridge.INVALID_MESSAGE'); | |
} | |
if (object.service) { | |
callbackContext = new CallbackContext(object.callbackId, event.source, event.origin); | |
service = Bridge.services[object.service]; | |
if (service) { | |
try { | |
result = service.execute(object.action, object.args, callbackContext); | |
} catch (exception) { | |
callbackContext.error('Bridge.ACTION_ERROR: ' + exception.message); | |
} | |
if (result !== undefined) { | |
callbackContext.success(result); | |
} | |
} else { | |
callbackContext.error('Bridge.INVALID_SERVICE'); | |
} | |
} else { | |
callbackId = object.callbackId; | |
if (Bridge.callbacks[callbackId]) { | |
callback = Bridge.callbacks[callbackId]; | |
if (object.status === PluginResult.Status.OK) { | |
if (jQuery.isFunction(callback.success)) { | |
try { | |
callback.success(object.message); | |
} catch (exception) { | |
console.error('Bridge.SUCCESS_CALLBACK_ERROR: ' + exception.message); | |
} | |
} | |
} else if (object.status === PluginResult.Status.ERROR) { | |
if (jQuery.isFunction(callback.error)) { | |
try { | |
callback.fail(object.message); | |
} catch (exception) { | |
console.error('Bridge.FAIL_CALLBACK_ERROR: ' + exception.message); | |
} | |
} | |
} | |
if (!object.keepCallback) { | |
delete Bridge.callbacks[callbackId]; | |
} | |
} | |
} | |
}); | |
function Iframe(options) { | |
this.$element = options.$element || jQuery('<iframe src="bridge.html"></iframe>'); | |
this.element = this.$element.get(0); | |
this.execQueue = []; | |
if (options) { | |
if (options.updateHeight) { | |
this.addEventListener('heightUpdate', jQuery.proxy(this.onHeightUpdate, this)); | |
} | |
if (options.mirror) { | |
var title = document.title, | |
js = 'document.title = \'' + title + '\';'; | |
if (jQuery.isFunction(history.pushState)) { | |
js += 'history.pushState(\'\', \'' + title + '\', \'' + location.href + '\');'; | |
} else { | |
js += 'location.replace(\'' + location.hash + '\');'; | |
} | |
this.eval(js); | |
} | |
} | |
if (!options.loaded) { | |
this.$element.load(jQuery.proxy(this.onLoad, this)); | |
} else { | |
this.loaded = true; | |
} | |
} | |
Iframe.prototype = { | |
$element: null, | |
element: null, | |
execQueue: null, | |
loaded: false, | |
exec: function (service, action, args, success, fail) { | |
if (this.loaded) { | |
exec(this.element.contentWindow, '*', success, fail, service, action, args); | |
} else { | |
this.execQueue.push(arguments); | |
} | |
}, | |
addEventListener: function (type, listener) { | |
return this.exec('core', 'addEventListener', [type], listener); | |
}, | |
removeEventListener: function (type, callbackId) { | |
this.exec('core', 'removeEventListener', [type, callbackId]); | |
}, | |
addStyle: function (css) { | |
this.exec('core', 'addStyle', [css]); | |
}, | |
append: function (html) { | |
this.exec('core', 'append', [html]); | |
}, | |
eval: function (source, success, fail) { | |
if (jQuery.isFunction(source)) { | |
source = '(' + source.toString() + '())'; | |
} | |
this.exec('core', 'eval', [source], success, fail); | |
}, | |
onLoad: function () { | |
var i, | |
queue = this.execQueue; | |
this.loaded = true; | |
for (i = queue.length - 1; i >= 0; --i) { | |
this.exec.apply(this, queue[i]); | |
} | |
}, | |
onHeightUpdate: function (height) { | |
this.$element.height(height); | |
} | |
}; | |
function PluginCore() { | |
addEvent(this, 'heightUpdate', { | |
intervalId: -1, | |
previousHeight: 0, | |
start: function () { | |
this.intervalId = setInterval(jQuery.proxy(this.intervalCallback, this), 500); | |
}, | |
intervalCallback: function () { | |
var height = jQuery('body').height(); | |
if (height !== this.previousHeight) { | |
this.previousHeight = height; | |
this.fireEvent(height); | |
} | |
}, | |
stop: function () { | |
clearInterval(this.intervalId); | |
} | |
}); | |
} | |
PluginCore.prototype = jQuery.extend(new Plugin(), { | |
addStyle: function (css) { | |
jQuery('head').append('<style type="text/css">' + css + '</style>'); | |
}, | |
append: function (html) { | |
jQuery('body').append(html); | |
}, | |
eval: function (source) { | |
return eval(source); // jshint ignore: line | |
} | |
}); | |
return jQuery.extend(Bridge, { | |
services: { | |
core: new PluginCore() | |
}, | |
exec: exec, | |
Iframe: Iframe | |
}); | |
}(this, jQuery)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment