Created
January 13, 2014 19:20
-
-
Save philipbreau/8406320 to your computer and use it in GitHub Desktop.
bridgeit.js with a few alerts for easier debugging
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
/* BridgeIt Mobile 1.0.0 | |
* | |
* Copyright 2004-2013 ICEsoft Technologies Canada Corp. | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the | |
* License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | |
* express or implied. See the License for the specific language | |
* governing permissions and limitations under the License. | |
*/ | |
if (!window['ice']) { | |
window.ice = {}; | |
} | |
if (!window['bridgeit']) { | |
window.bridgeit = {}; | |
window.bridgeIt = window.bridgeit; //alias bridgeit and bridgeIt | |
} | |
if (!window.console) { | |
console = {}; | |
if (ice.logInContainer) { | |
console.log = ice.logInContainer; | |
} else { | |
console.log = function() { | |
}; | |
console.error = function() { | |
}; | |
} | |
} | |
/** | |
* The BridgeIt JavaScript API. Native Mobile integration for your web app. | |
* | |
* BridgeIt provides a variety of device commands that allow access to | |
* device features from JavaScript, all while running in the stock browser | |
* such as Safari or Chrome. This is made possible by the BridgeIt utilty app | |
* that runs alongside the browser and is available for each of the supported | |
* platforms (currently Android, iOS, and Windows Phone 8). | |
* | |
* For example, bridgeit.camera('myCamera', 'myCallback') will allow the user | |
* to take a photo identified by 'myCamera' and this will be returned via an | |
* event to the function named myCallback. For the best compatibility the | |
* callback is passed by name since the browser page may be refreshed when | |
* the callback returns. The callback will be passed an event where: | |
* | |
* event.response: HTTP response from the server if the command makes an HTTP POST | |
* event.preview: data-uri containing any preview image resulting from the command | |
* event.name: id specified in the command call | |
* event.value: return value from the command | |
* | |
* Most device commands accept an options parameter object. Options supported | |
* by a variety of commands are: options.postURL (the URL used to upload | |
* the result of the command), and extra parameters | |
* specific to the command may be added to the options argument. | |
* | |
* @class bridgeit | |
*/ | |
(function(b) { | |
/* *********************** PRIVATE ******************************/ | |
function serializeForm(formId, typed) { | |
var form = document.getElementById(formId); | |
var els = form.elements; | |
var len = els.length; | |
var qString = []; | |
var addField = function(name, value) { | |
var tmpStr = ""; | |
if (qString.length > 0) { | |
tmpStr = "&"; | |
} | |
tmpStr += encodeURIComponent(name) + "=" + encodeURIComponent(value); | |
qString.push(tmpStr); | |
}; | |
for (var i = 0; i < len; i++) { | |
var el = els[i]; | |
if (!el.disabled) { | |
var prefix = ""; | |
if (typed) { | |
var vtype = el.getAttribute("data-type"); | |
if (vtype) { | |
prefix = vtype + "-"; | |
} else { | |
prefix = el.type + "-"; | |
} | |
} | |
switch (el.type) { | |
case 'submit': | |
case 'button': | |
case 'fieldset': | |
break; | |
case 'text': | |
case 'password': | |
case 'hidden': | |
case 'textarea': | |
addField(prefix + el.name, el.value); | |
break; | |
case 'select-one': | |
if (el.selectedIndex >= 0) { | |
addField(prefix + el.name, el.options[el.selectedIndex].value); | |
} | |
break; | |
case 'select-multiple': | |
for (var j = 0; j < el.options.length; j++) { | |
if (el.options[j].selected) { | |
addField(prefix + el.name, el.options[j].value); | |
} | |
} | |
break; | |
case 'checkbox': | |
case 'radio': | |
if (el.checked) { | |
addField(prefix + el.name, el.value); | |
} | |
break; | |
default: | |
addField(prefix + el.name, el.value); | |
} | |
} | |
} | |
// concatenate the array | |
return qString.join(""); | |
} | |
var useBase64 = false; | |
if (window.jQuery && jQuery.mobile) { | |
//jquery mobile insists on parsing BridgeIt hashchange data | |
useBase64 = true;; | |
} | |
function getDeviceCommand() { | |
var commandData = null; | |
var sxkey = "#icemobilesx"; | |
var sxlen = sxkey.length; | |
var locHash = "" + window.location.hash; | |
if (sxkey === locHash.substring(0, sxlen)) { | |
commandData = locHash.substring(sxlen + 1); | |
var dupIndex = commandData.indexOf(sxkey); | |
if (dupIndex > 0) { | |
commandData = commandData.substring(0, dupIndex); | |
console.error("trimmed corrupt " + locHash + " to " | |
+ commandData); | |
} | |
} | |
return commandData; | |
} | |
var reservedParams = ['postURL', 'element', 'form', 'deviceCommandCallback']; | |
function deviceCommandExec(command, id, options) { | |
console.log("deviceCommandExec('" + command + "', '" + id + ", " + JSON.stringify(options)); | |
var ampchar = String.fromCharCode(38); | |
var uploadURL; | |
var sessionid; | |
var params; | |
var element; | |
var formID; | |
var callback; | |
if (options) { | |
if (options.postURL) { | |
uploadURL = options.postURL; | |
} | |
params = packObject(options, reservedParams); | |
if (options.deviceCommandCallback) { | |
callback = options.deviceCommandCallback; | |
if ("string" != typeof(callback)) { | |
if (bridgeit.allowAnonymousCallbacks) { | |
callback = "!anon"; | |
} else { | |
console.error( | |
"BridgeIt callbacks must be named in window scope"); | |
callback = null; | |
} | |
} | |
} | |
if (options.element) { | |
element = options.element; | |
} | |
if (options.form) { | |
formID = options.form.getAttribute("id"); | |
} | |
} | |
if (!uploadURL) { | |
uploadURL = getUploadURL(element); | |
} | |
var windowLocation = window.location; | |
var barURL = windowLocation.toString(); | |
var baseURL = barURL.substring(0, | |
barURL.lastIndexOf("/")) + "/"; | |
var returnURL = "" + window.location; | |
var lastHash = returnURL.lastIndexOf("#"); | |
var theHash = ""; | |
var theURL = returnURL; | |
if (lastHash > 0) { | |
theHash = returnURL.substring(lastHash); | |
theURL = returnURL.substring(0, lastHash); | |
} | |
returnURL = theURL + "#icemobilesx"; | |
var hashSubClause = ""; | |
if (!!theHash) { | |
hashSubClause = "&h=" + escape(theHash); | |
} | |
var callbackClause = ""; | |
if (!!callback) { | |
callbackClause = "&c=" + escape(callback); | |
} | |
seqClause = "&seq=" + (new Date()).getTime(); | |
var hashClause = ""; | |
if (!!hashSubClause || !!callbackClause) { | |
hashClause = "&h=" + escape(hashSubClause) + escape(callbackClause) | |
+ escape(seqClause); | |
} | |
deviceOptions = null; | |
if (useBase64) { | |
//jquery mobile insists on parsing BridgeIt hashchange data | |
deviceOptions = "enc=base64"; | |
} | |
var optionsClause = ""; | |
if (!!deviceOptions) { | |
optionsClause = "&o=" + escape(deviceOptions); | |
} | |
if (params && ("" != params)) { | |
params = "&ub=" + escape(baseURL) + ampchar + params; | |
} | |
console.log('params = ' + params); | |
var sessionidClause = ""; | |
if (sessionid && ("" != sessionid)) { | |
sessionidClause = "&JSESSIONID=" + escape(sessionid); | |
//also need PHPSESSID and ASPSESSIONID | |
} | |
var serializedFormClause = ""; | |
if (formID && ("" != formID)) { | |
serializedFormClause = "&p=" + | |
escape(serializeForm(formID, false)); | |
} | |
var uploadURLClause = ""; | |
if (uploadURL && ("" != uploadURL)) { | |
uploadURLClause = "&u=" + escape(uploadURL); | |
} | |
var sxURL = "c=" + escape(command + | |
"?id=" + id + ampchar + (params ? params : '')) + | |
uploadURLClause + | |
"&r=" + escape(returnURL) + | |
sessionidClause + | |
optionsClause + | |
hashClause + | |
serializedFormClause; | |
if (b.isWindowsPhone8()) { | |
sxURL = escape(sxURL); | |
} | |
var sxBase = "icemobile:"; | |
if (b.isAndroid()) { | |
sxBase = "http://bridgeit.mobi/android/install/index.html#" | |
} | |
sxURL = sxBase + sxURL; | |
console.log('sxURL=' + sxURL); | |
window.location = sxURL; | |
} | |
function getSplashClause() { | |
var splashClause = ""; | |
if (null != bridgeit.splashImageURL) { | |
var splashImage = "i=" + escape(bridgeit.splashImageURL); | |
splashClause = "&s=" + escape(splashImage); | |
} | |
return splashClause; | |
} | |
var autoDetectUploadURL = false; | |
function getUploadURL(element) { | |
if (!autoDetectUploadURL) { | |
return null; | |
} | |
var uploadURL; | |
var windowLocation = window.location; | |
var barURL = windowLocation.toString(); | |
var baseURL = barURL.substring(0, | |
barURL.lastIndexOf("/")) + "/"; | |
if (!element) { | |
uploadURL = baseURL; | |
} else { | |
var form = formOf(element); | |
formID = form.getAttribute('id'); | |
var formAction = form.getAttribute("action"); | |
if (!uploadURL) { | |
uploadURL = element.getAttribute("data-posturl"); | |
} | |
if (!uploadURL) { | |
if (0 === formAction.indexOf("/")) { | |
uploadURL = window.location.origin + formAction; | |
} else if ((0 === formAction.indexOf("http://")) || | |
(0 === formAction.indexOf("https://"))) { | |
uploadURL = formAction; | |
} else { | |
uploadURL = baseURL + formAction; | |
} | |
} | |
} | |
return uploadURL; | |
} | |
var checkTimeout; | |
function deviceCommand(command, id, callback, options) { | |
if( !b.isSupportedPlatform(command) ){ | |
b.notSupported(id, command); | |
return; | |
} | |
if (b.isIOS()) { | |
checkTimeout = setTimeout( function() { | |
bridgeit.launchFailed(id); | |
}, 3000); | |
} | |
if (!options) { | |
options = {}; | |
} | |
console.log(command + " " + id); | |
bridgeit.deviceCommandCallback = callback; | |
options.deviceCommandCallback = callback; | |
deviceCommandExec(command, id, options); | |
} | |
function setInput(target, name, value, vtype) { | |
console.log('setInput(target=' + target + ', name=' + name + ', value=' + value + ', vtype=' + vtype); | |
var hiddenID = name + "-hid"; | |
var existing = document.getElementById(hiddenID); | |
if (existing) { | |
existing.setAttribute("value", value); | |
return; | |
} | |
var targetElm = document.getElementById(target); | |
if (!targetElm) { | |
return; | |
} | |
var hidden = document.createElement("input"); | |
hidden.setAttribute("type", "hidden"); | |
hidden.setAttribute("id", hiddenID); | |
hidden.setAttribute("name", name); | |
hidden.setAttribute("value", value); | |
if (vtype) { | |
hidden.setAttribute("data-type", vtype); | |
} | |
targetElm.parentNode.insertBefore(hidden, targetElm); | |
} | |
function formOf(element) { | |
var parent = element; | |
while (null != parent) { | |
if ("form" == parent.nodeName.toLowerCase()) { | |
return parent; | |
} | |
parent = parent.parentNode; | |
} | |
} | |
function packObject(params, exclude) { | |
var packed = ""; | |
var sep = ""; | |
for (var key in params) { | |
if (exclude.indexOf(key) < 0) { | |
packed += sep + escape(key) + "=" + escape(params[key]); | |
sep = "&"; | |
} | |
} | |
return packed; | |
} | |
function unpackDeviceResponse(data) { | |
var result = {}; | |
if (useBase64 && (data.indexOf("!") < 0)) { | |
data = data.replace(/~/g,"="); | |
data = data.replace(/\./g,"/"); | |
data = decodeURIComponent(atob(data)); | |
} | |
var params = data.split("&"); | |
var len = params.length; | |
for (var i = 0; i < len; i++) { | |
var splitIndex = params[i].indexOf("="); | |
var paramName = unescape(params[i].substring(0, splitIndex)); | |
var paramValue = decodeURIComponent( | |
params[i].substring(splitIndex + 1) ); | |
if ("!" === paramName.substring(0,1)) { | |
//BridgeIt parameters are set directly | |
result[paramName.substring(1)] = paramValue; | |
} else { | |
//only one user value is supported | |
result.name = paramName; | |
result.value = paramValue; | |
} | |
} | |
return result; | |
} | |
function url2Object(encoded) { | |
var parts = encoded.split("&"); | |
var record = {}; | |
for (var i = 0; i < parts.length; i++) { | |
if (!!parts[i]) { | |
var pair = parts[i].split("="); | |
record[unescape(pair[0])] = decodeURIComponent(pair[1]); | |
} | |
} | |
return record; | |
} | |
function getNamedObject(name) { | |
var parts = name.split("."); | |
var theObject = window; | |
for (var i = 0; i < parts.length; i++) { | |
theObject = theObject[parts[i]]; | |
if (!theObject) { | |
return null; | |
} | |
} | |
if (window == theObject) { | |
return null; | |
} | |
return theObject; | |
} | |
function addOnLoadListener(func) { | |
var oldonload = window.onload; | |
window.onload = function() { | |
try { | |
if (oldonload) { | |
oldonload(); | |
} | |
} catch (e) { | |
console.error(e); | |
} | |
func(); | |
} | |
} | |
var isDataPending = false; | |
var isLoaded = false; | |
var pendingData = null; | |
function loadComplete() { | |
isLoaded = true; | |
} | |
function checkExecDeviceResponse() { | |
var data = getDeviceCommand(); | |
if (null == data) { | |
data = pendingData; | |
//record URL/hash changes that are not device commands | |
storeLastPage(); | |
} | |
var deviceParams; | |
if (null != data) { | |
pendingData = data; | |
isDataPending = true; | |
if (!isLoaded) { | |
console.log("checkExecDeviceResponse waiting for onload"); | |
return; | |
} | |
var name; | |
var value; | |
var needRefresh = true; | |
if ("" != data) { | |
deviceParams = unpackDeviceResponse(data); | |
if (deviceParams.name) { | |
name = deviceParams.name; | |
value = deviceParams.value; | |
setInput(name, name, value); | |
needRefresh = false; | |
} | |
} | |
if (needRefresh) { | |
console.log('needs refresh'); | |
if (window.ice.ajaxRefresh) { | |
ice.ajaxRefresh(); | |
} | |
} | |
setTimeout( function(){ | |
if (!isDataPending) { | |
alert("checkExecDeviceResponse is done, exiting"); | |
return; | |
} | |
var sxEvent = { | |
name : name, | |
value : value | |
}; | |
var callback = bridgeit.deviceCommandCallback; | |
alert('sxEvent: ' + JSON.stringify(sxEvent) + " " + | |
JSON.stringify(deviceParams)); | |
var restoreHash = ""; | |
if (deviceParams) { | |
if (deviceParams.r) { | |
sxEvent.response = deviceParams.r; | |
} | |
if (deviceParams.p) { | |
sxEvent.preview = deviceParams.p; | |
} | |
if (deviceParams.c) { | |
setCloudPushId(deviceParams.c); | |
if (ice.push) { | |
ice.push.parkInactivePushIds( | |
deviceParams.c ); | |
} | |
} | |
if (deviceParams.h) { | |
var echoed = url2Object(unescape(deviceParams.h)); | |
if (echoed.h) { | |
restoreHash = echoed.h; | |
} | |
if (echoed.c) { | |
var namedCallBack = getNamedObject(echoed.c); | |
if (namedCallBack) { | |
callback = namedCallBack; | |
} | |
} | |
} | |
} | |
var loc = window.location; | |
isDataPending = false; | |
pendingData = null; | |
if( !hasInstalledToken() ){ | |
setInstalledToken(); | |
} | |
if (callback) { | |
try { | |
callback(sxEvent); | |
setTimeout(function(){ | |
var restoreLocation = | |
loc.pathname + loc.search + restoreHash; | |
history.replaceState("", document.title, | |
restoreLocation); | |
alert('bridgeit history replaceState: ' + | |
restoreLocation); | |
},100); | |
} catch (e) { | |
alert("Device function callback failed " + e); | |
alert(e.stack); | |
} | |
bridgeit.deviceCommandCallback = null; | |
} else{ | |
alert('no deviceCommandCallback registered :('); | |
} | |
}, 1); | |
} | |
} | |
var CLOUD_PUSH_KEY = "ice.notifyBack"; | |
function setCloudPushId(id) { | |
//rely on local storage since cloud push is on modern platforms | |
if (localStorage) { | |
localStorage.setItem(CLOUD_PUSH_KEY, id); | |
} | |
} | |
function getCloudPushId() { | |
if (localStorage) { | |
return localStorage.getItem(CLOUD_PUSH_KEY); | |
} | |
return null; | |
} | |
function setupCloudPush() { | |
var cloudPushId = getCloudPushId(); | |
if (!!cloudPushId) { | |
if (ice.push) { | |
console.log("Cloud Push registered: " + cloudPushId); | |
ice.push.parkInactivePushIds(cloudPushId); | |
} | |
} | |
} | |
//move pause and resume to ICEpush when ready | |
function pausePush() { | |
if (window.ice && ice.push) { | |
ice.push.connection.pauseConnection(); | |
} | |
} | |
function resumePush() { | |
if (window.ice && ice.push) { | |
ice.push.connection.resumeConnection(); | |
resumePushGroups(); | |
} | |
} | |
function resumePushGroups() { | |
for (var pushID in pushListeners) { | |
var pushListener = pushListeners[pushID]; | |
console.log("rejoining push group with old pushid " + | |
pushListener.group + " " + pushID ); | |
ice.push.addGroupMember(pushListener.group, pushID); | |
} | |
} | |
var LAST_PAGE_KEY = "bridgeit.lastpage"; | |
function storeLastPage(lastPage) { | |
if (!lastPage) { | |
var sxkey = "#icemobilesx"; | |
var sxlen = sxkey.length; | |
var locHash = "" + window.location.hash; | |
lastPage = "" + document.location; | |
if (sxkey === locHash.substring(0, sxlen)) { | |
lastPage = lastPage.substring(0, | |
lastPage.length - locHash.length) | |
} | |
} | |
if (localStorage) { | |
localStorage.setItem(LAST_PAGE_KEY, lastPage); | |
console.log("bridgeit storeLastPage " + lastPage); | |
} | |
} | |
/* Page event handling */ | |
if (window.addEventListener) { | |
window.addEventListener("pagehide", function () { | |
//hiding the page either indicates user does not require | |
//BridgeIt or the url scheme invocation has succeeded | |
clearTimeout(checkTimeout); | |
if (ice.push && ice.push.connection) { | |
pausePush(); | |
} | |
}, false); | |
window.addEventListener("pageshow", function () { | |
if (ice.push && ice.push.connection) { | |
resumePush(); | |
} | |
}, false); | |
window.addEventListener("hashchange", function () { | |
alert('entered hashchange listener hash=' + window.location.hash); | |
checkExecDeviceResponse(); | |
}, false); | |
window.addEventListener("load", function () { | |
storeLastPage(); | |
}, false); | |
document.addEventListener("webkitvisibilitychange", function () { | |
if (document.webkitHidden) { | |
pausePush(); | |
} else { | |
resumePush(); | |
} | |
}); | |
document.addEventListener("visibilitychange", function () { | |
if (document.hidden) { | |
pausePush(); | |
} else { | |
resumePush(); | |
} | |
}); | |
}; | |
function httpGET(uri, query) { | |
var xhr = new XMLHttpRequest(); | |
var queryStr = ""; | |
if (!!query) { | |
queryStr = "?" + query; | |
} | |
xhr.open('GET', uri + queryStr, false); | |
xhr.send(query); | |
if (xhr.status == 200) { | |
return xhr.responseText; | |
} else { | |
throw xhr.statusText + '[' + xhr.status + ']'; | |
} | |
} | |
function endsWith(s, pattern) { | |
return s.lastIndexOf(pattern) == s.length - pattern.length; | |
} | |
var absoluteGoBridgeItURL = null; | |
function fetchGoBridgeIt(url) { | |
var xhr = new XMLHttpRequest(); | |
xhr.onreadystatechange = function() { | |
if (4 == xhr.readyState) { | |
if (200 == xhr.status) { | |
if (!absoluteGoBridgeItURL) { | |
absoluteGoBridgeItURL = getAbsoluteURL(url); | |
console.log("Cloud Push return via goBridgeIt: " + | |
absoluteGoBridgeItURL); | |
} | |
} | |
} | |
}; | |
xhr.open('GET', url, true); | |
xhr.send(); | |
} | |
function findGoBridgeIt() { | |
if (!!bridgeit.goBridgeItURL) { | |
//page setting overrides detection | |
absoluteGoBridgeItURL = getAbsoluteURL(bridgeit.goBridgeItURL); | |
return; | |
} | |
//host-wide page | |
fetchGoBridgeIt('/goBridgeIt.html'); | |
//application-specific page | |
fetchGoBridgeIt('goBridgeIt.html'); | |
} | |
function getAbsoluteURL(url) { | |
var img = document.createElement('img'); | |
img.src = url; | |
url = img.src; | |
return url; | |
} | |
function loadPushService(uri, apikey) { | |
if (ice && ice.push) { | |
console.log('Push service already loaded and configured'); | |
} else { | |
var baseURI = uri + (endsWith(uri, '/') ? '' : '/'); | |
var codeURI = baseURI + 'code.icepush'; | |
var code = httpGET(codeURI); | |
eval(code); | |
ice.push.configuration.contextPath = baseURI; | |
ice.push.configuration.apikey = apikey; | |
ice.push.connection.startConnection(); | |
findGoBridgeIt(); | |
} | |
setupCloudPush(); | |
} | |
var pushListeners = {}; | |
function addPushListenerImpl(group, callback) { | |
if (ice && ice.push && ice.push.configuration.contextPath) { | |
ice.push.connection.resumeConnection(); | |
var pushId = ice.push.createPushId(); | |
pushListeners[pushId] = {group: group, callback: callback}; | |
ice.push.addGroupMember(group, pushId); | |
if ("string" != typeof(callback)) { | |
console.error( | |
"BridgeIt Cloud Push callbacks must be named in window scope"); | |
} else { | |
var callbackName = callback; | |
callback = getNamedObject(callback); | |
if (!!callback) { | |
if (localStorage) { | |
var callbacks = localStorage | |
.getItem(CLOUD_CALLBACKS_KEY); | |
if (!callbacks) { | |
callbacks = " "; | |
} | |
if (callbacks.indexOf(" " + callbackName + " ") < 0) { | |
callbacks += callbackName + " "; | |
} | |
localStorage.setItem(CLOUD_CALLBACKS_KEY, callbacks); | |
} | |
} | |
} | |
ice.push.register([ pushId ], callback); | |
} else { | |
console.error('Push service is not active'); | |
} | |
}; | |
var BRIDGEIT_INSTALLED_KEY = "bridgeit.installed"; | |
var BRIDGEIT_INSTALLED_LOG_KEY = "bridgeit.installedLogged"; | |
function hasInstalledToken(){ | |
var result = false; | |
if( window.localStorage){ | |
var installTimestamp = localStorage.getItem(BRIDGEIT_INSTALLED_KEY); | |
if( installTimestamp ){ | |
if( !window.sessionStorage.getItem(BRIDGEIT_INSTALLED_LOG_KEY) ){ | |
console.log('bridgeit installed ' | |
+ new Date( parseInt(localStorage.getItem(BRIDGEIT_INSTALLED_KEY))).toGMTString()); | |
window.sessionStorage.setItem(BRIDGEIT_INSTALLED_LOG_KEY, 'true'); | |
} | |
result = true; | |
} | |
} | |
return result; | |
} | |
function setInstalledToken(){ | |
if( window.localStorage ){ | |
localStorage.setItem(BRIDGEIT_INSTALLED_KEY, '' + new Date().getTime()); | |
} | |
} | |
/* *********************** PUBLIC **********************************/ | |
/** | |
* Application provided callback to detect BridgeIt launch failure. | |
* This should be overridden with an implementation that prompts the | |
* user to download BridgeIt and potentially fallback with a different | |
* browser control such as input file. | |
* | |
* @alias plugin.launchFailed | |
* @param {String} id The id passed to the command that failed | |
* @template | |
*/ | |
b.launchFailed = function(id) { | |
alert("BridgeIt not available for " + id); | |
}; | |
/** | |
* Application provided callback to detect non-supported clients. | |
* This should be overridden with an implementation that informs the | |
* user the user that native mobile functionality is only available | |
* on supported platforms or potentially fallback with a different | |
* browser control such as input file, which would be available on | |
* all browsers. | |
* @param {String} id The id passed to the command that failed | |
* @param {String} command The BridgeIt api command that was launched | |
* @alias plugin.notSupported | |
* @template | |
*/ | |
b.notSupported = function(id, command) { | |
alert('Sorry, the command ' + command + ' for BridgeIt is not supported on this platform'); | |
}; | |
/** | |
* Launch the device QR Code scanner. | |
* | |
* The callback function will be called once the scan is captured. | |
* The return value will be set to the text resulting from the scan. | |
* | |
* The QR Code scanner does not currently accept additional parameters, | |
* but these may used in the future. | |
* | |
* @alias plugin.scan | |
* @param {String} id The id of the return value | |
* @param {Function} callback The callback function. | |
* @param {Object} options Additional command options | |
* @param {String} options.postURL Server-side URL accepting POST of command result (optional) | |
* | |
*/ | |
b.scan = function(id, callback, options) { | |
deviceCommand("scan", id, callback, options); | |
}; | |
/** | |
* Launch the native camera. | |
* | |
* The callback function will be called once the photo is captured. | |
* | |
* @alias plugin.camera | |
* @param {String} id The id of the return value | |
* @param {Function} callback The callback function. | |
* @param {Object} options Additional command options | |
* @param {String} options.postURL Server-side URL accepting POST of command result (optional) | |
* @param {Object} options.maxwidth The maxium width for the image in pixels | |
* @param {Object} options.maxheight The maxium height for the image in pixels | |
* | |
*/ | |
b.camera = function(id, callback, options) { | |
deviceCommand("camera", id, callback, options); | |
}; | |
/** | |
* Launch the native video recorder. | |
* | |
* The callback function will be called once the video has been captured. | |
* | |
* @alias plugin.camcorder | |
* @param {String} id The id of the return value | |
* @param {Function} callback The callback function. | |
* @param {Object} options Additional command options | |
* | |
*/ | |
b.camcorder = function(id, callback, options) { | |
deviceCommand("camcorder", id, callback, options); | |
}; | |
/** | |
* Launch the native audio recorder. | |
* | |
* The callback function will be called once the audio is captured. | |
* | |
* @alias plugin.microphone | |
* @param {String} id The id of the return value | |
* @param {Function} callback The callback function. | |
* @param {Object} options Additional command options | |
* | |
*/ | |
b.microphone = function(id, callback, options) { | |
deviceCommand("microphone", id, callback, options); | |
}; | |
/** | |
* Launch the native contact list. | |
* | |
* The callback function will be called once the contact is retrieved. | |
* | |
* @alias plugin.fetchContact | |
* @param {String} id The id of the return value | |
* @param {Function} callback The callback function. | |
* @param {Object} options Additional command options | |
* @param {Object} options.fields The contact fields to retrieve, default = "name,email,phone" | |
* | |
*/ | |
b.fetchContact = function(id, callback, options) { | |
var ops = options || {}; | |
ops.fields = "name,email,phone"; | |
deviceCommand("fetchContacts", id, callback, ops); | |
}; | |
/** | |
* Send an SMS message. | |
* | |
* The sms function will send an SMS message to a number on supported | |
* platforms. On iOS devices, a native SMS call is made through the | |
* BridgeIt utility app. On other platforms an SMS URL protocol is used in a | |
* DOM anchor element, which the browser may use to launch the device | |
* SMS functionality, if available. | |
* | |
* @alias plugin.sms | |
* @param {String} number The phone number to send the message to | |
* @param {String} message The message | |
* | |
*/ | |
b.sms = function(number, message) { | |
if( !b.isSupportedPlatform('sms') ){ | |
b.notSupported(null, 'sms'); | |
return; | |
} | |
if( number == 'undefined' || number == '') | |
return; | |
if( b.isIOS()){ | |
deviceCommand('sms', '_sms', null, {n: number, body: message}); | |
} | |
else{ | |
var smsBtn = document.createElement('a'); | |
var cleanNumber = number.replace(/[\s-\.\+]/g,''); | |
smsBtn.href = 'sms:+' + cleanNumber + '?body=' + encodeURI(message); | |
smsBtn.style = 'display:none'; | |
document.body.appendChild(smsBtn); | |
smsBtn.click(); | |
document.removeChild(smsBtn); | |
} | |
}; | |
/** | |
* Launch an Augmented Reality view. | |
* | |
* The Augmented Reality view displays a set of geographic icons on | |
* a video overlay. The icons are positioned according to the | |
* orientation of the device so that they appear in a line-of-sight | |
* with their physical geographic position. The user can select an | |
* icon and this is relayed back to the application. | |
* | |
* The callback function will be called once the augmented reality | |
* view exits with the user selection provided in the return value. | |
* The command is invoked with a locations parameter containing an | |
* array of named locations, each with a comma-separated latitude, | |
* longitude, altitude, direction, and icon URL | |
* | |
* @alias plugin.augmentedReality | |
* @param {String} id The id of the return value | |
* @param {Function} callback The callback function. | |
* @param {Object} options Additional command options | |
* @param {Object} options.locations The augmented reality locations to display | |
* | |
*/ | |
b.augmentedReality = function(id, callback, options) { | |
//copy locations directly into options. The JavaScript API | |
//will not change, but the future deviceCommand will accept | |
//the locations as a subparameter to avoid this copying | |
if (options && options.locations) { | |
for (var key in options.locations) { | |
if (reservedParams.indexOf(key) < 0) { | |
options[key] = options.locations[key]; | |
} | |
} | |
delete options.locations; | |
} | |
deviceCommand("aug", id, callback, options); | |
}; | |
/** | |
* Activate location tracking. | |
* | |
* Location tracking will run in the | |
* background according to the specified strategy and duration, and will POST | |
* a geoJSON record to the specified postURL. | |
* | |
* Three strategies are currently supported: "continuous" where the location | |
* of the device will be uploaded as frequently as it changes (intended for | |
* testing only due to high power consumption), "significant" where the | |
* location is uploaded when it changes significantly, and "stop" to cease | |
* location tracking. | |
* | |
* The callback function will be called once location tracking is activated. | |
* | |
* @param {String} id The id of the return value | |
* @param {Function} callback The callback function. | |
* @param {Object} options Additional command options | |
* @param {String} options.postURL The URL accepting the geoJSON POST | |
* @param {String} options.strategy The strategy, "continuous", "significant" or "stop" | |
* @param {String} options.duration The duration in hours | |
* @alias plugin.geoTrack | |
* | |
*/ | |
b.geoTrack = function(id, callback, options) { | |
deviceCommand("geospy", id, callback, options); | |
}; | |
/** | |
* Register BridgeIt integration and configure Cloud Push. | |
* | |
* This call is necessary to obtain the Cloud Push ID of the | |
* device so that notifications can be delivered when the | |
* user is not currently viewing your application in the browser. | |
* | |
* The callback function will be called when Cloud Push registration | |
* completes. | |
* | |
* @alias plugin.register | |
* @inheritdoc #scan | |
* | |
*/ | |
b.register = function(id, callback, options) { | |
deviceCommand("register", id, callback, options); | |
}; | |
/** | |
* Verify that BridgeIt Cloud Push is registered. | |
* | |
* @alias plugin.isRegistered | |
* | |
*/ | |
b.isRegistered = function() { | |
return !!(getCloudPushId()); | |
}; | |
/** | |
* Utility method to unpack url-encoded parameters into an object. | |
* | |
* @alias plugin.url2Object | |
* @param {String} encoded The encoded URL string to unpack | |
*/ | |
b.url2Object = function(encoded) { | |
return url2Object(encoded); | |
}; | |
/** | |
* Set allowAnonymousCallbacks to true to take advantage of persistent | |
* callback functions currently supported on iOS. | |
* @property {Boolean} [allowAnonymousCallbacks=false] | |
*/ | |
b.allowAnonymousCallbacks = false; | |
/** | |
* Is the current browser iOS | |
* @alias plugin.isIOS | |
*/ | |
b.isIOS = function(){ | |
var i = 0, | |
iOS = false, | |
iDevice = ['iPad', 'iPhone', 'iPod']; | |
for ( ; i < iDevice.length ; i++ ) { | |
if( navigator.userAgent.indexOf(iDevice[i]) > -1 ){ | |
iOS = true; break; | |
} | |
} | |
return iOS; | |
}; | |
/** | |
* Is the current client an iPhone | |
* @alias plugin.isIPhone | |
*/ | |
b.isIPhone = function(){ | |
return navigator.userAgent.indexOf('iPhone') > -1; | |
}; | |
/** | |
* Is the current browser iOS 6 | |
* @alias plugin.isIOS6 | |
*/ | |
b.isIOS6 = function(){ | |
return /(iPad|iPhone|iPod).*OS 6_/.test( navigator.userAgent ); | |
}; | |
/** | |
* Is the current browser iOS 7 | |
* @alias plugin.isIOS7 | |
*/ | |
b.isIOS7 = function(){ | |
return /(iPad|iPhone|iPod).*OS 7_/.test( navigator.userAgent ); | |
}; | |
/** | |
* Is the current browser Android | |
* @alias plugin.isAndroid | |
*/ | |
b.isAndroid = function(){ | |
return navigator.userAgent.toLowerCase() | |
.indexOf("android") > -1; | |
}; | |
/** | |
* Is the current browser Android | |
* @alias plugin.isAndroidFroyo | |
*/ | |
b.isAndroidFroyo = function(){ | |
return navigator.userAgent.indexOf("Android 2.2") > -1; | |
}; | |
/** | |
* Is the current browser Android | |
* @alias plugin.isAndroidGingerBreadOrGreater | |
*/ | |
b.isAndroidGingerBreadOrGreater = function(){ | |
return b.isAndroid() && !b.isAndroidFroyo(); | |
}; | |
/** | |
* Is the current browser Windows Phone 8 | |
* @alias plugin.isWindowsPhone8 | |
*/ | |
b.isWindowsPhone8 = function(){ | |
var ua = navigator.userAgent; | |
return ua.indexOf('IEMobile') > -1 | |
|| ( ua.indexOf('MSIE 10') > -1 | |
&& typeof window.orientation !== 'undefined'); | |
}; | |
var android = b.isAndroid(); | |
var supportedAndroid = b.isAndroidGingerBreadOrGreater(); | |
var iOS = b.isIOS(); | |
var iOS6 = b.isIOS6(); | |
var iOS7 = b.isIOS7(); | |
var wp8 = b.isWindowsPhone8(); | |
var iPhone = b.isIPhone(); | |
var commands = ['camera','camcorder','microphone','fetchContacts','aug','push','scan','geospy','sms']; | |
var fullySupported = [true, true, true, true, true, true, true, true, true]; | |
var supportMatrix = { | |
'iPhone':{ | |
'6': [true, true, true, true, true, true, false, true, true], | |
'7': fullySupported | |
}, | |
'iPad-iPod':{ | |
'6': [true, true, true, true, true, true, false, true, false], | |
'7': [true, true, true, true, true, true, true, true, false] | |
}, | |
'wp8': [true, true, true, true, false, false, true, false, true], | |
'android': [true, true, true, true, false, true, true, true, true] | |
} | |
/** | |
* Set to true to have Augmented Reality (in experimental status) be used on Android (default false) | |
* @property overrideAugmentedRealityAlphaLevel | |
*/ | |
b.overrideAugmentedRealityAlphaLevel = false; | |
/** | |
* Check if the current browser is supported by the BridgeIt Native Mobile app. | |
* | |
* Currently iOS, Android, and some features on Windows Phone 8 are supported. | |
* @param {String} command The BridgeIt API command that may or may not be supported | |
* @alias plugin.isSupportedPlatform | |
*/ | |
b.isSupportedPlatform = function(command){ | |
if( 'register' == command ){ | |
return true; //do not check platform for cloud push registration | |
} | |
var supported = false; | |
if( android ){ | |
if( supportedAndroid ){ | |
if( 'aug' == command ){ | |
supported = b.overrideAugmentedRealityAlphaLevel; | |
} | |
else{ | |
supported = true; | |
} | |
} | |
} | |
else if( wp8 ){ | |
return supportMatrix['wp8'][commands.indexOf(command)]; | |
} | |
else if( iOS ){ | |
if( iPhone ){ | |
if( iOS6 ){ | |
return supportMatrix['iPhone']['6'][commands.indexOf(command)]; | |
} | |
else if( iOS7 ){ | |
return supportMatrix['iPhone']['7'][commands.indexOf(command)]; | |
} | |
} | |
else { | |
if( iOS6 ){ | |
return supportMatrix['iPad-iPod']['6'][commands.indexOf(command)]; | |
} | |
else if( iOS7 ){ | |
return supportMatrix['iPad-iPod']['7'][commands.indexOf(command)]; | |
} | |
} | |
} | |
console.log("bridgeIt supported platform for '" + command + "' command: " + supported); | |
return supported; | |
}; | |
/** | |
* Returns the app store URL to BridgeIt for the appropirate platform | |
* @alias plugin.appStoreURL | |
*/ | |
b.appStoreURL = function(){ | |
if( b.isAndroid() ) { | |
return 'https://play.google.com/store/apps/details?id=mobi.bridgeit'; | |
} | |
else if( b.isIOS() ) { | |
return 'https://itunes.apple.com/app/bridgeit/id727736414'; | |
} | |
else if( b.isWindowsPhone8() ) { | |
return 'http://windowsphone.com/s?appId=b9a1b29f-2b30-4e5d-9bf1-f75e773d74e1'; | |
} | |
}; | |
var jguid; | |
/** | |
* Returns a persistent id that allows an application to persistently maintain information for | |
* an individual user without requiring a server-side session. | |
* @alias plugin.getId | |
*/ | |
b.getId = function() { | |
var JGUID_KEY = "bridgeit.jguid"; | |
if (!jguid) { | |
if (localStorage) { | |
jguid = localStorage.getItem(JGUID_KEY); | |
} | |
if (!jguid) { | |
jguid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, | |
function(c) { | |
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); | |
return v.toString(16); | |
}); | |
if (localStorage) { | |
localStorage.setItem(JGUID_KEY, jguid); | |
} | |
} | |
} | |
return jguid; | |
} | |
/** | |
* Set goBridgeItURL to the URL of your goBridgeIt.html file | |
* to allow {@link bridgeit#push Cloud Push} to go back to the most recent page | |
* The defaults of the host root and the current relative | |
* directory URL do not need to be specified. For an example, see | |
* http://bridgeit.mobi/demo/goBridgeIt.html | |
* | |
* @property {String} [goBridgeItURL] | |
*/ | |
b.goBridgeItURL = null; | |
var CLOUD_CALLBACKS_KEY = "bridgeit.cloudcallbacks"; | |
/** | |
* Public callback used by Cloud Push implementation | |
* to relay push event to a newly opened browser window. | |
* This API is not for application use. | |
* @alias plugin.handleCloudPush | |
* @private | |
*/ | |
b.handleCloudPush = function () { | |
var callbacks = localStorage.getItem(CLOUD_CALLBACKS_KEY); | |
var parts = callbacks.split(" "); | |
var callback; | |
for (var i = 0; i < parts.length; i++) { | |
callback = getNamedObject(parts[i]); | |
if (callback) { | |
callback(); | |
} | |
} | |
}; | |
/** | |
* Configure Push service and connect to it. | |
* @alias plugin.usePushService | |
* @param uri the location of the service | |
* @param apikey | |
*/ | |
b.usePushService = function(uri, apikey) { | |
window.setTimeout(function() { | |
loadPushService(uri, apikey); | |
}, 1); | |
}; | |
/** | |
* Add listner for notifications belonging to the specified group. | |
* Callbacks must be passed by name to receive cloud push notifications, | |
* regardless of bridgeit.allowAnonymousCallbacks setting | |
* @param group | |
* @param callback | |
* @alias plugin.addPushListener | |
*/ | |
b.addPushListener = function(group, callback) { | |
window.setTimeout(function() { | |
addPushListenerImpl(group, callback); | |
}, 1); | |
}; | |
/** | |
* Augment a URL so that callbacks will be invoked upon Cloud Push | |
* return. | |
* If called with no argument, the current URL is used. | |
* @param url | |
* @alias plugin.cloudPushReturnURL | |
*/ | |
b.cloudPushReturnURL = function(url) { | |
if (!url) { | |
if (localStorage) { | |
url = localStorage[LAST_PAGE_KEY]; | |
} | |
} | |
if (!url) { | |
url = window.location.href; | |
} | |
var seq = (new Date()).getTime(); | |
var urlExtra = | |
btoa("!h=" + escape("c=bridgeit.handleCloudPush&seq=" + seq)); | |
urlExtra = urlExtra.replace(/=/g,"~"); | |
urlExtra = urlExtra.replace(/\//g,"."); | |
var returnURL = url + "#icemobilesx_" + urlExtra; | |
return returnURL; | |
}; | |
/** | |
* Push notification to the group. | |
* | |
* This will result in an Ajax Push (and associated callback) | |
* to any web pages that have added a push listener to the | |
* specified group. If Cloud Push options are provided | |
* (options.subject and options.detail) a Cloud Push will | |
* be dispatched as a home screen notification to any devices | |
* unable to recieve the Ajax Push via the web page. | |
* | |
* @param {String} groupName The Ajax Push group name to push to | |
* @param {Object} options Options that a notification can carry | |
* @param {String} options.subject The subject heading for the notification | |
* @param {String} options.message The message text to be sent in the notification body | |
* @alias plugin.push | |
*/ | |
b.push = function(groupName, options) { | |
if (!absoluteGoBridgeItURL) { | |
if (!!bridgeit.goBridgeItURL) { | |
absoluteGoBridgeItURL = getAbsoluteURL(bridgeit.goBridgeItURL); | |
} | |
} | |
if (!!absoluteGoBridgeItURL) { | |
if (options && !options.url) { | |
options.url = absoluteGoBridgeItURL; | |
} | |
} | |
if (ice && ice.push && ice.push.configuration.contextPath) { | |
console.log("bridgeit.push " + JSON.stringify(options)); | |
if (options && options.delay) { | |
ice.push.notify(groupName, options, options); | |
} else { | |
ice.push.notify(groupName, options); | |
} | |
} else { | |
console.error('Push service is not active'); | |
} | |
}; | |
//android functions as full page load | |
addOnLoadListener(loadComplete); | |
addOnLoadListener(checkExecDeviceResponse); | |
checkExecDeviceResponse(); | |
})(bridgeit); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment