X-Git-Url: https://git.mdrn.pl/wl-mobile.git/blobdiff_plain/8b7b42d45674861437a31e9f9125a277ce998721..065d23f9494e1afb7f61ada08b728405b1b7a0cf:/assets/www/js/phonegap-1.0.0.js diff --git a/assets/www/js/phonegap-1.0.0.js b/assets/www/js/phonegap-1.0.0.js new file mode 100644 index 0000000..751c52c --- /dev/null +++ b/assets/www/js/phonegap-1.0.0.js @@ -0,0 +1,4367 @@ +/* + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2005-2010, Nitobi Software Inc. + * Copyright (c) 2010-2011, IBM Corporation + */ + +if (typeof PhoneGap === "undefined") { + +/** + * The order of events during page load and PhoneGap startup is as follows: + * + * onDOMContentLoaded Internal event that is received when the web page is loaded and parsed. + * window.onload Body onload event. + * onNativeReady Internal event that indicates the PhoneGap native side is ready. + * onPhoneGapInit Internal event that kicks off creation of all PhoneGap JavaScript objects (runs constructors). + * onPhoneGapReady Internal event fired when all PhoneGap JavaScript objects have been created + * onPhoneGapInfoReady Internal event fired when device properties are available + * onDeviceReady User event fired to indicate that PhoneGap is ready + * onResume User event fired to indicate a start/resume lifecycle event + * onPause User event fired to indicate a pause lifecycle event + * onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one). + * + * The only PhoneGap events that user code should register for are: + * onDeviceReady + * onResume + * + * Listeners can be registered as: + * document.addEventListener("deviceready", myDeviceReadyListener, false); + * document.addEventListener("resume", myResumeListener, false); + * document.addEventListener("pause", myPauseListener, false); + */ + +if (typeof(DeviceInfo) !== 'object') { + var DeviceInfo = {}; +} + +/** + * This represents the PhoneGap API itself, and provides a global namespace for accessing + * information about the state of PhoneGap. + * @class + */ +var PhoneGap = { + queue: { + ready: true, + commands: [], + timer: null + } +}; + +/** + * List of resource files loaded by PhoneGap. + * This is used to ensure JS and other files are loaded only once. + */ +PhoneGap.resources = {base: true}; + +/** + * Determine if resource has been loaded by PhoneGap + * + * @param name + * @return + */ +PhoneGap.hasResource = function(name) { + return PhoneGap.resources[name]; +}; + +/** + * Add a resource to list of loaded resources by PhoneGap + * + * @param name + */ +PhoneGap.addResource = function(name) { + PhoneGap.resources[name] = true; +}; + +/** + * Custom pub-sub channel that can have functions subscribed to it + * @constructor + */ +PhoneGap.Channel = function (type) +{ + this.type = type; + this.handlers = {}; + this.guid = 0; + this.fired = false; + this.enabled = true; +}; + +/** + * Subscribes the given function to the channel. Any time that + * Channel.fire is called so too will the function. + * Optionally specify an execution context for the function + * and a guid that can be used to stop subscribing to the channel. + * Returns the guid. + */ +PhoneGap.Channel.prototype.subscribe = function(f, c, g) { + // need a function to call + if (f === null) { return; } + + var func = f; + if (typeof c === "object" && typeof f === "function") { func = PhoneGap.close(c, f); } + + g = g || func.observer_guid || f.observer_guid || this.guid++; + func.observer_guid = g; + f.observer_guid = g; + this.handlers[g] = func; + return g; +}; + +/** + * Like subscribe but the function is only called once and then it + * auto-unsubscribes itself. + */ +PhoneGap.Channel.prototype.subscribeOnce = function(f, c) { + var g = null; + var _this = this; + var m = function() { + f.apply(c || null, arguments); + _this.unsubscribe(g); + }; + if (this.fired) { + if (typeof c === "object" && typeof f === "function") { f = PhoneGap.close(c, f); } + f.apply(this, this.fireArgs); + } else { + g = this.subscribe(m); + } + return g; +}; + +/** + * Unsubscribes the function with the given guid from the channel. + */ +PhoneGap.Channel.prototype.unsubscribe = function(g) { + if (typeof g === "function") { g = g.observer_guid; } + this.handlers[g] = null; + delete this.handlers[g]; +}; + +/** + * Calls all functions subscribed to this channel. + */ +PhoneGap.Channel.prototype.fire = function(e) { + if (this.enabled) { + var fail = false; + var item, handler, rv; + for (item in this.handlers) { + if (this.handlers.hasOwnProperty(item)) { + handler = this.handlers[item]; + if (typeof handler === "function") { + rv = (handler.apply(this, arguments) === false); + fail = fail || rv; + } + } + } + this.fired = true; + this.fireArgs = arguments; + return !fail; + } + return true; +}; + +/** + * Calls the provided function only after all of the channels specified + * have been fired. + */ +PhoneGap.Channel.join = function(h, c) { + var i = c.length; + var f = function() { + if (!(--i)) { + h(); + } + }; + var len = i; + var j; + for (j=0; j + * + * @param name The plugin name + * @param obj The plugin object + */ +PhoneGap.addPlugin = function(name, obj) { + if (!window.plugins[name]) { + window.plugins[name] = obj; + } + else { + console.log("Error: Plugin "+name+" already exists."); + } +}; + +/** + * onDOMContentLoaded channel is fired when the DOM content + * of the page has been parsed. + */ +PhoneGap.onDOMContentLoaded = new PhoneGap.Channel('onDOMContentLoaded'); + +/** + * onNativeReady channel is fired when the PhoneGap native code + * has been initialized. + */ +PhoneGap.onNativeReady = new PhoneGap.Channel('onNativeReady'); + +/** + * onPhoneGapInit channel is fired when the web page is fully loaded and + * PhoneGap native code has been initialized. + */ +PhoneGap.onPhoneGapInit = new PhoneGap.Channel('onPhoneGapInit'); + +/** + * onPhoneGapReady channel is fired when the JS PhoneGap objects have been created. + */ +PhoneGap.onPhoneGapReady = new PhoneGap.Channel('onPhoneGapReady'); + +/** + * onPhoneGapInfoReady channel is fired when the PhoneGap device properties + * has been set. + */ +PhoneGap.onPhoneGapInfoReady = new PhoneGap.Channel('onPhoneGapInfoReady'); + +/** + * onPhoneGapConnectionReady channel is fired when the PhoneGap connection properties + * has been set. + */ +PhoneGap.onPhoneGapConnectionReady = new PhoneGap.Channel('onPhoneGapConnectionReady'); + +/** + * onResume channel is fired when the PhoneGap native code + * resumes. + */ +PhoneGap.onResume = new PhoneGap.Channel('onResume'); + +/** + * onPause channel is fired when the PhoneGap native code + * pauses. + */ +PhoneGap.onPause = new PhoneGap.Channel('onPause'); + +/** + * onDestroy channel is fired when the PhoneGap native code + * is destroyed. It is used internally. + * Window.onunload should be used by the user. + */ +PhoneGap.onDestroy = new PhoneGap.Channel('onDestroy'); +PhoneGap.onDestroy.subscribeOnce(function() { + PhoneGap.shuttingDown = true; +}); +PhoneGap.shuttingDown = false; + +// _nativeReady is global variable that the native side can set +// to signify that the native code is ready. It is a global since +// it may be called before any PhoneGap JS is ready. +if (typeof _nativeReady !== 'undefined') { PhoneGap.onNativeReady.fire(); } + +/** + * onDeviceReady is fired only after all PhoneGap objects are created and + * the device properties are set. + */ +PhoneGap.onDeviceReady = new PhoneGap.Channel('onDeviceReady'); + + +// Array of channels that must fire before "deviceready" is fired +PhoneGap.deviceReadyChannelsArray = [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady, PhoneGap.onPhoneGapConnectionReady]; + +// Hashtable of user defined channels that must also fire before "deviceready" is fired +PhoneGap.deviceReadyChannelsMap = {}; + +/** + * Indicate that a feature needs to be initialized before it is ready to be used. + * This holds up PhoneGap's "deviceready" event until the feature has been initialized + * and PhoneGap.initComplete(feature) is called. + * + * @param feature {String} The unique feature name + */ +PhoneGap.waitForInitialization = function(feature) { + if (feature) { + var channel = new PhoneGap.Channel(feature); + PhoneGap.deviceReadyChannelsMap[feature] = channel; + PhoneGap.deviceReadyChannelsArray.push(channel); + } +}; + +/** + * Indicate that initialization code has completed and the feature is ready to be used. + * + * @param feature {String} The unique feature name + */ +PhoneGap.initializationComplete = function(feature) { + var channel = PhoneGap.deviceReadyChannelsMap[feature]; + if (channel) { + channel.fire(); + } +}; + +/** + * Create all PhoneGap objects once page has fully loaded and native side is ready. + */ +PhoneGap.Channel.join(function() { + + // Start listening for XHR callbacks + setTimeout(function() { + if (PhoneGap.UsePolling) { + PhoneGap.JSCallbackPolling(); + } + else { + var polling = prompt("usePolling", "gap_callbackServer:"); + PhoneGap.UsePolling = polling; + if (polling == "true") { + PhoneGap.UsePolling = true; + PhoneGap.JSCallbackPolling(); + } + else { + PhoneGap.UsePolling = false; + PhoneGap.JSCallback(); + } + } + }, 1); + + // Run PhoneGap constructors + PhoneGap.onPhoneGapInit.fire(); + + // Fire event to notify that all objects are created + PhoneGap.onPhoneGapReady.fire(); + + // Fire onDeviceReady event once all constructors have run and PhoneGap info has been + // received from native side, and any user defined initialization channels. + PhoneGap.Channel.join(function() { + PhoneGap.onDeviceReady.fire(); + + // Fire the onresume event, since first one happens before JavaScript is loaded + PhoneGap.onResume.fire(); + }, PhoneGap.deviceReadyChannelsArray); + +}, [ PhoneGap.onDOMContentLoaded, PhoneGap.onNativeReady ]); + +// Listen for DOMContentLoaded and notify our channel subscribers +document.addEventListener('DOMContentLoaded', function() { + PhoneGap.onDOMContentLoaded.fire(); +}, false); + +// Intercept calls to document.addEventListener and watch for deviceready +PhoneGap.m_document_addEventListener = document.addEventListener; + +document.addEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + if (e === 'deviceready') { + PhoneGap.onDeviceReady.subscribeOnce(handler); + } else if (e === 'resume') { + PhoneGap.onResume.subscribe(handler); + if (PhoneGap.onDeviceReady.fired) { + PhoneGap.onResume.fire(); + } + } else if (e === 'pause') { + PhoneGap.onPause.subscribe(handler); + } + else { + // If subscribing to Android backbutton + if (e === 'backbutton') { + PhoneGap.exec(null, null, "App", "overrideBackbutton", [true]); + } + + PhoneGap.m_document_addEventListener.call(document, evt, handler, capture); + } +}; + +// Intercept calls to document.removeEventListener and watch for events that +// are generated by PhoneGap native code +PhoneGap.m_document_removeEventListener = document.removeEventListener; + +document.removeEventListener = function(evt, handler, capture) { + var e = evt.toLowerCase(); + + // If unsubscribing to Android backbutton + if (e === 'backbutton') { + PhoneGap.exec(null, null, "App", "overrideBackbutton", [false]); + } + + PhoneGap.m_document_removeEventListener.call(document, evt, handler, capture); +}; + +/** + * Method to fire event from native code + */ +PhoneGap.fireEvent = function(type) { + var e = document.createEvent('Events'); + e.initEvent(type); + document.dispatchEvent(e); +}; + +/** + * If JSON not included, use our own stringify. (Android 1.6) + * The restriction on ours is that it must be an array of simple types. + * + * @param args + * @return {String} + */ +PhoneGap.stringify = function(args) { + if (typeof JSON === "undefined") { + var s = "["; + var i, type, start, name, nameType, a; + for (i = 0; i < args.length; i++) { + if (args[i] !== null) { + if (i > 0) { + s = s + ","; + } + type = typeof args[i]; + if ((type === "number") || (type === "boolean")) { + s = s + args[i]; + } else if (args[i] instanceof Array) { + s = s + "[" + args[i] + "]"; + } else if (args[i] instanceof Object) { + start = true; + s = s + '{'; + for (name in args[i]) { + if (args[i][name] !== null) { + if (!start) { + s = s + ','; + } + s = s + '"' + name + '":'; + nameType = typeof args[i][name]; + if ((nameType === "number") || (nameType === "boolean")) { + s = s + args[i][name]; + } else if ((typeof args[i][name]) === 'function') { + // don't copy the functions + s = s + '""'; + } else if (args[i][name] instanceof Object) { + s = s + PhoneGap.stringify(args[i][name]); + } else { + s = s + '"' + args[i][name] + '"'; + } + start = false; + } + } + s = s + '}'; + } else { + a = args[i].replace(/\\/g, '\\\\'); + a = a.replace(/"/g, '\\"'); + s = s + '"' + a + '"'; + } + } + } + s = s + "]"; + return s; + } else { + return JSON.stringify(args); + } +}; + +/** + * Does a deep clone of the object. + * + * @param obj + * @return {Object} + */ +PhoneGap.clone = function(obj) { + var i, retVal; + if(!obj) { + return obj; + } + + if(obj instanceof Array){ + retVal = []; + for(i = 0; i < obj.length; ++i){ + retVal.push(PhoneGap.clone(obj[i])); + } + return retVal; + } + + if (typeof obj === "function") { + return obj; + } + + if(!(obj instanceof Object)){ + return obj; + } + + if (obj instanceof Date) { + return obj; + } + + retVal = {}; + for(i in obj){ + if(!(i in retVal) || retVal[i] !== obj[i]) { + retVal[i] = PhoneGap.clone(obj[i]); + } + } + return retVal; +}; + +PhoneGap.callbackId = 0; +PhoneGap.callbacks = {}; +PhoneGap.callbackStatus = { + NO_RESULT: 0, + OK: 1, + CLASS_NOT_FOUND_EXCEPTION: 2, + ILLEGAL_ACCESS_EXCEPTION: 3, + INSTANTIATION_EXCEPTION: 4, + MALFORMED_URL_EXCEPTION: 5, + IO_EXCEPTION: 6, + INVALID_ACTION: 7, + JSON_EXCEPTION: 8, + ERROR: 9 + }; + + +/** + * Execute a PhoneGap command. It is up to the native side whether this action is synch or async. + * The native side can return: + * Synchronous: PluginResult object as a JSON string + * Asynchrounous: Empty string "" + * If async, the native side will PhoneGap.callbackSuccess or PhoneGap.callbackError, + * depending upon the result of the action. + * + * @param {Function} success The success callback + * @param {Function} fail The fail callback + * @param {String} service The name of the service to use + * @param {String} action Action to be run in PhoneGap + * @param {Array.} [args] Zero or more arguments to pass to the method + */ +PhoneGap.exec = function(success, fail, service, action, args) { + try { + var callbackId = service + PhoneGap.callbackId++; + if (success || fail) { + PhoneGap.callbacks[callbackId] = {success:success, fail:fail}; + } + + var r = prompt(PhoneGap.stringify(args), "gap:"+PhoneGap.stringify([service, action, callbackId, true])); + + // If a result was returned + if (r.length > 0) { + eval("var v="+r+";"); + + // If status is OK, then return value back to caller + if (v.status === PhoneGap.callbackStatus.OK) { + + // If there is a success callback, then call it now with + // returned value + if (success) { + try { + success(v.message); + } catch (e) { + console.log("Error in success callback: " + callbackId + " = " + e); + } + + // Clear callback if not expecting any more results + if (!v.keepCallback) { + delete PhoneGap.callbacks[callbackId]; + } + } + return v.message; + } + + // If no result + else if (v.status === PhoneGap.callbackStatus.NO_RESULT) { + + // Clear callback if not expecting any more results + if (!v.keepCallback) { + delete PhoneGap.callbacks[callbackId]; + } + } + + // If error, then display error + else { + console.log("Error: Status="+v.status+" Message="+v.message); + + // If there is a fail callback, then call it now with returned value + if (fail) { + try { + fail(v.message); + } + catch (e1) { + console.log("Error in error callback: "+callbackId+" = "+e1); + } + + // Clear callback if not expecting any more results + if (!v.keepCallback) { + delete PhoneGap.callbacks[callbackId]; + } + } + return null; + } + } + } catch (e2) { + console.log("Error: "+e2); + } +}; + +/** + * Called by native code when returning successful result from an action. + * + * @param callbackId + * @param args + */ +PhoneGap.callbackSuccess = function(callbackId, args) { + if (PhoneGap.callbacks[callbackId]) { + + // If result is to be sent to callback + if (args.status === PhoneGap.callbackStatus.OK) { + try { + if (PhoneGap.callbacks[callbackId].success) { + PhoneGap.callbacks[callbackId].success(args.message); + } + } + catch (e) { + console.log("Error in success callback: "+callbackId+" = "+e); + } + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete PhoneGap.callbacks[callbackId]; + } + } +}; + +/** + * Called by native code when returning error result from an action. + * + * @param callbackId + * @param args + */ +PhoneGap.callbackError = function(callbackId, args) { + if (PhoneGap.callbacks[callbackId]) { + try { + if (PhoneGap.callbacks[callbackId].fail) { + PhoneGap.callbacks[callbackId].fail(args.message); + } + } + catch (e) { + console.log("Error in error callback: "+callbackId+" = "+e); + } + + // Clear callback if not expecting any more results + if (!args.keepCallback) { + delete PhoneGap.callbacks[callbackId]; + } + } +}; + + +/** + * Internal function used to dispatch the request to PhoneGap. It processes the + * command queue and executes the next command on the list. If one of the + * arguments is a JavaScript object, it will be passed on the QueryString of the + * url, which will be turned into a dictionary on the other end. + * @private + */ +// TODO: Is this used? +PhoneGap.run_command = function() { + if (!PhoneGap.available || !PhoneGap.queue.ready) { + return; + } + PhoneGap.queue.ready = false; + + var args = PhoneGap.queue.commands.shift(); + if (PhoneGap.queue.commands.length === 0) { + clearInterval(PhoneGap.queue.timer); + PhoneGap.queue.timer = null; + } + + var uri = []; + var dict = null; + var i; + for (i = 1; i < args.length; i++) { + var arg = args[i]; + if (arg === undefined || arg === null) { + arg = ''; + } + if (typeof(arg) === 'object') { + dict = arg; + } else { + uri.push(encodeURIComponent(arg)); + } + } + var url = "gap://" + args[0] + "/" + uri.join("/"); + if (dict !== null) { + var name; + var query_args = []; + for (name in dict) { + if (dict.hasOwnProperty(name) && (typeof (name) === 'string')) { + query_args.push(encodeURIComponent(name) + "=" + encodeURIComponent(dict[name])); + } + } + if (query_args.length > 0) { + url += "?" + query_args.join("&"); + } + } + document.location = url; + +}; + +PhoneGap.JSCallbackPort = null; +PhoneGap.JSCallbackToken = null; + +/** + * This is only for Android. + * + * Internal function that uses XHR to call into PhoneGap Java code and retrieve + * any JavaScript code that needs to be run. This is used for callbacks from + * Java to JavaScript. + */ +PhoneGap.JSCallback = function() { + + // Exit if shutting down app + if (PhoneGap.shuttingDown) { + return; + } + + // If polling flag was changed, start using polling from now on + if (PhoneGap.UsePolling) { + PhoneGap.JSCallbackPolling(); + return; + } + + var xmlhttp = new XMLHttpRequest(); + + // Callback function when XMLHttpRequest is ready + xmlhttp.onreadystatechange=function(){ + if(xmlhttp.readyState === 4){ + + // Exit if shutting down app + if (PhoneGap.shuttingDown) { + return; + } + + // If callback has JavaScript statement to execute + if (xmlhttp.status === 200) { + + // Need to url decode the response + var msg = decodeURIComponent(xmlhttp.responseText); + setTimeout(function() { + try { + var t = eval(msg); + } + catch (e) { + // If we're getting an error here, seeing the message will help in debugging + console.log("JSCallback: Message from Server: " + msg); + console.log("JSCallback Error: "+e); + } + }, 1); + setTimeout(PhoneGap.JSCallback, 1); + } + + // If callback ping (used to keep XHR request from timing out) + else if (xmlhttp.status === 404) { + setTimeout(PhoneGap.JSCallback, 10); + } + + // If security error + else if (xmlhttp.status === 403) { + console.log("JSCallback Error: Invalid token. Stopping callbacks."); + } + + // If server is stopping + else if (xmlhttp.status === 503) { + console.log("JSCallback Error: Service unavailable. Stopping callbacks."); + } + + // If request wasn't GET + else if (xmlhttp.status === 400) { + console.log("JSCallback Error: Bad request. Stopping callbacks."); + } + + // If error, revert to polling + else { + console.log("JSCallback Error: Request failed."); + PhoneGap.UsePolling = true; + PhoneGap.JSCallbackPolling(); + } + } + }; + + if (PhoneGap.JSCallbackPort === null) { + PhoneGap.JSCallbackPort = prompt("getPort", "gap_callbackServer:"); + } + if (PhoneGap.JSCallbackToken === null) { + PhoneGap.JSCallbackToken = prompt("getToken", "gap_callbackServer:"); + } + xmlhttp.open("GET", "http://127.0.0.1:"+PhoneGap.JSCallbackPort+"/"+PhoneGap.JSCallbackToken , true); + xmlhttp.send(); +}; + +/** + * The polling period to use with JSCallbackPolling. + * This can be changed by the application. The default is 50ms. + */ +PhoneGap.JSCallbackPollingPeriod = 50; + +/** + * Flag that can be set by the user to force polling to be used or force XHR to be used. + */ +PhoneGap.UsePolling = false; // T=use polling, F=use XHR + +/** + * This is only for Android. + * + * Internal function that uses polling to call into PhoneGap Java code and retrieve + * any JavaScript code that needs to be run. This is used for callbacks from + * Java to JavaScript. + */ +PhoneGap.JSCallbackPolling = function() { + + // Exit if shutting down app + if (PhoneGap.shuttingDown) { + return; + } + + // If polling flag was changed, stop using polling from now on + if (!PhoneGap.UsePolling) { + PhoneGap.JSCallback(); + return; + } + + var msg = prompt("", "gap_poll:"); + if (msg) { + setTimeout(function() { + try { + var t = eval(""+msg); + } + catch (e) { + console.log("JSCallbackPolling: Message from Server: " + msg); + console.log("JSCallbackPolling Error: "+e); + } + }, 1); + setTimeout(PhoneGap.JSCallbackPolling, 1); + } + else { + setTimeout(PhoneGap.JSCallbackPolling, PhoneGap.JSCallbackPollingPeriod); + } +}; + +/** + * Create a UUID + * + * @return {String} + */ +PhoneGap.createUUID = function() { + return PhoneGap.UUIDcreatePart(4) + '-' + + PhoneGap.UUIDcreatePart(2) + '-' + + PhoneGap.UUIDcreatePart(2) + '-' + + PhoneGap.UUIDcreatePart(2) + '-' + + PhoneGap.UUIDcreatePart(6); +}; + +PhoneGap.UUIDcreatePart = function(length) { + var uuidpart = ""; + var i, uuidchar; + for (i=0; i frequency + 10 sec + PhoneGap.exec( + function(timeout) { + if (timeout < (frequency + 10000)) { + PhoneGap.exec(null, null, "Accelerometer", "setTimeout", [frequency + 10000]); + } + }, + function(e) { }, "Accelerometer", "getTimeout", []); + + // Start watch timer + var id = PhoneGap.createUUID(); + navigator.accelerometer.timers[id] = setInterval(function() { + PhoneGap.exec(successCallback, errorCallback, "Accelerometer", "getAcceleration", []); + }, (frequency ? frequency : 1)); + + return id; +}; + +/** + * Clears the specified accelerometer watch. + * + * @param {String} id The id of the watch returned from #watchAcceleration. + */ +Accelerometer.prototype.clearWatch = function(id) { + + // Stop javascript timer & remove from timer list + if (id && navigator.accelerometer.timers[id] !== undefined) { + clearInterval(navigator.accelerometer.timers[id]); + delete navigator.accelerometer.timers[id]; + } +}; + +PhoneGap.addConstructor(function() { + if (typeof navigator.accelerometer === "undefined") { + navigator.accelerometer = new Accelerometer(); + } +}); +} + + +/* + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2005-2010, Nitobi Software Inc. + * Copyright (c) 2010-2011, IBM Corporation + */ + +if (!PhoneGap.hasResource("app")) { +PhoneGap.addResource("app"); +(function() { + +/** + * Constructor + * @constructor + */ +var App = function() {}; + +/** + * Clear the resource cache. + */ +App.prototype.clearCache = function() { + PhoneGap.exec(null, null, "App", "clearCache", []); +}; + +/** + * Load the url into the webview. + * + * @param url The URL to load + * @param props Properties that can be passed in to the activity: + * wait: int => wait msec before loading URL + * loadingDialog: "Title,Message" => display a native loading dialog + * hideLoadingDialogOnPage: boolean => hide loadingDialog when page loaded instead of when deviceready event occurs. + * loadInWebView: boolean => cause all links on web page to be loaded into existing web view, instead of being loaded into new browser. + * loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error + * errorUrl: URL => URL to load if there's an error loading specified URL with loadUrl(). Should be a local URL such as file:///android_asset/www/error.html"); + * keepRunning: boolean => enable app to keep running in background + * + * Example: + * App app = new App(); + * app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000}); + */ +App.prototype.loadUrl = function(url, props) { + PhoneGap.exec(null, null, "App", "loadUrl", [url, props]); +}; + +/** + * Cancel loadUrl that is waiting to be loaded. + */ +App.prototype.cancelLoadUrl = function() { + PhoneGap.exec(null, null, "App", "cancelLoadUrl", []); +}; + +/** + * Clear web history in this web view. + * Instead of BACK button loading the previous web page, it will exit the app. + */ +App.prototype.clearHistory = function() { + PhoneGap.exec(null, null, "App", "clearHistory", []); +}; + +/** + * Override the default behavior of the Android back button. + * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. + * + * Note: The user should not have to call this method. Instead, when the user + * registers for the "backbutton" event, this is automatically done. + * + * @param override T=override, F=cancel override + */ +App.prototype.overrideBackbutton = function(override) { + PhoneGap.exec(null, null, "App", "overrideBackbutton", [override]); +}; + +/** + * Exit and terminate the application. + */ +App.prototype.exitApp = function() { + return PhoneGap.exec(null, null, "App", "exitApp", []); +}; + +PhoneGap.addConstructor(function() { + navigator.app = new App(); +}); +}()); +} + + +/* + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2005-2010, Nitobi Software Inc. + * Copyright (c) 2010-2011, IBM Corporation + */ + +if (!PhoneGap.hasResource("camera")) { +PhoneGap.addResource("camera"); + +/** + * This class provides access to the device camera. + * + * @constructor + */ +var Camera = function() { + this.successCallback = null; + this.errorCallback = null; + this.options = null; +}; + +/** + * Format of image that returned from getPicture. + * + * Example: navigator.camera.getPicture(success, fail, + * { quality: 80, + * destinationType: Camera.DestinationType.DATA_URL, + * sourceType: Camera.PictureSourceType.PHOTOLIBRARY}) + */ +Camera.DestinationType = { + DATA_URL: 0, // Return base64 encoded string + FILE_URI: 1 // Return file uri (content://media/external/images/media/2 for Android) +}; +Camera.prototype.DestinationType = Camera.DestinationType; + +/** + * Encoding of image returned from getPicture. + * + * Example: navigator.camera.getPicture(success, fail, + * { quality: 80, + * destinationType: Camera.DestinationType.DATA_URL, + * sourceType: Camera.PictureSourceType.CAMERA, + * encodingType: Camera.EncodingType.PNG}) +*/ +Camera.EncodingType = { + JPEG: 0, // Return JPEG encoded image + PNG: 1 // Return PNG encoded image +}; +Camera.prototype.EncodingType = Camera.EncodingType; + +/** + * Source to getPicture from. + * + * Example: navigator.camera.getPicture(success, fail, + * { quality: 80, + * destinationType: Camera.DestinationType.DATA_URL, + * sourceType: Camera.PictureSourceType.PHOTOLIBRARY}) + */ +Camera.PictureSourceType = { + PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android) + CAMERA : 1, // Take picture from camera + SAVEDPHOTOALBUM : 2 // Choose image from picture library (same as PHOTOLIBRARY for Android) +}; +Camera.prototype.PictureSourceType = Camera.PictureSourceType; + +/** + * Gets a picture from source defined by "options.sourceType", and returns the + * image as defined by the "options.destinationType" option. + + * The defaults are sourceType=CAMERA and destinationType=DATA_URL. + * + * @param {Function} successCallback + * @param {Function} errorCallback + * @param {Object} options + */ +Camera.prototype.getPicture = function(successCallback, errorCallback, options) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Camera Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Camera Error: errorCallback is not a function"); + return; + } + + this.options = options; + var quality = 80; + if (options.quality) { + quality = this.options.quality; + } + + var maxResolution = 0; + if (options.maxResolution) { + maxResolution = this.options.maxResolution; + } + + var destinationType = Camera.DestinationType.DATA_URL; + if (this.options.destinationType) { + destinationType = this.options.destinationType; + } + var sourceType = Camera.PictureSourceType.CAMERA; + if (typeof this.options.sourceType === "number") { + sourceType = this.options.sourceType; + } + var encodingType = Camera.EncodingType.JPEG; + if (typeof options.encodingType == "number") { + encodingType = this.options.encodingType; + } + + var targetWidth = -1; + if (typeof options.targetWidth == "number") { + targetWidth = options.targetWidth; + } else if (typeof options.targetWidth == "string") { + var width = new Number(options.targetWidth); + if (isNaN(width) === false) { + targetWidth = width.valueOf(); + } + } + + var targetHeight = -1; + if (typeof options.targetHeight == "number") { + targetHeight = options.targetHeight; + } else if (typeof options.targetHeight == "string") { + var height = new Number(options.targetHeight); + if (isNaN(height) === false) { + targetHeight = height.valueOf(); + } + } + + PhoneGap.exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType]); +}; + +PhoneGap.addConstructor(function() { + if (typeof navigator.camera === "undefined") { + navigator.camera = new Camera(); + } +}); +} + + +/* + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2005-2010, Nitobi Software Inc. + * Copyright (c) 2010-2011, IBM Corporation + */ + +if (!PhoneGap.hasResource("capture")) { +PhoneGap.addResource("capture"); + +/** + * Represents a single file. + * + * name {DOMString} name of the file, without path information + * fullPath {DOMString} the full path of the file, including the name + * type {DOMString} mime type + * lastModifiedDate {Date} last modified date + * size {Number} size of the file in bytes + */ +var MediaFile = function(name, fullPath, type, lastModifiedDate, size){ + this.name = name || null; + this.fullPath = fullPath || null; + this.type = type || null; + this.lastModifiedDate = lastModifiedDate || null; + this.size = size || 0; +}; + +/** + * Launch device camera application for recording video(s). + * + * @param {Function} successCB + * @param {Function} errorCB + */ +MediaFile.prototype.getFormatData = function(successCallback, errorCallback){ + PhoneGap.exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]); +}; + +/** + * MediaFileData encapsulates format information of a media file. + * + * @param {DOMString} codecs + * @param {long} bitrate + * @param {long} height + * @param {long} width + * @param {float} duration + */ +var MediaFileData = function(codecs, bitrate, height, width, duration){ + this.codecs = codecs || null; + this.bitrate = bitrate || 0; + this.height = height || 0; + this.width = width || 0; + this.duration = duration || 0; +}; + +/** + * The CaptureError interface encapsulates all errors in the Capture API. + */ +var CaptureError = function(){ + this.code = null; +}; + +// Capture error codes +CaptureError.CAPTURE_INTERNAL_ERR = 0; +CaptureError.CAPTURE_APPLICATION_BUSY = 1; +CaptureError.CAPTURE_INVALID_ARGUMENT = 2; +CaptureError.CAPTURE_NO_MEDIA_FILES = 3; +CaptureError.CAPTURE_NOT_SUPPORTED = 20; + +/** + * The Capture interface exposes an interface to the camera and microphone of the hosting device. + */ +var Capture = function(){ + this.supportedAudioModes = []; + this.supportedImageModes = []; + this.supportedVideoModes = []; +}; + +/** + * Launch audio recorder application for recording audio clip(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureAudioOptions} options + */ +Capture.prototype.captureAudio = function(successCallback, errorCallback, options){ + PhoneGap.exec(successCallback, errorCallback, "Capture", "captureAudio", [options]); +}; + +/** + * Launch camera application for taking image(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureImageOptions} options + */ +Capture.prototype.captureImage = function(successCallback, errorCallback, options){ + PhoneGap.exec(successCallback, errorCallback, "Capture", "captureImage", [options]); +}; + +/** + * Launch camera application for taking image(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureImageOptions} options + */ +Capture.prototype._castMediaFile = function(pluginResult){ + var mediaFiles = []; + var i; + for (i = 0; i < pluginResult.message.length; i++) { + var mediaFile = new MediaFile(); + mediaFile.name = pluginResult.message[i].name; + mediaFile.fullPath = pluginResult.message[i].fullPath; + mediaFile.type = pluginResult.message[i].type; + mediaFile.lastModifiedDate = pluginResult.message[i].lastModifiedDate; + mediaFile.size = pluginResult.message[i].size; + mediaFiles.push(mediaFile); + } + pluginResult.message = mediaFiles; + return pluginResult; +}; + +/** + * Launch device camera application for recording video(s). + * + * @param {Function} successCB + * @param {Function} errorCB + * @param {CaptureVideoOptions} options + */ +Capture.prototype.captureVideo = function(successCallback, errorCallback, options){ + PhoneGap.exec(successCallback, errorCallback, "Capture", "captureVideo", [options]); +}; + +/** + * Encapsulates a set of parameters that the capture device supports. + */ +var ConfigurationData = function(){ + // The ASCII-encoded string in lower case representing the media type. + this.type = null; + // The height attribute represents height of the image or video in pixels. + // In the case of a sound clip this attribute has value 0. + this.height = 0; + // The width attribute represents width of the image or video in pixels. + // In the case of a sound clip this attribute has value 0 + this.width = 0; +}; + +/** + * Encapsulates all image capture operation configuration options. + */ +var CaptureImageOptions = function(){ + // Upper limit of images user can take. Value must be equal or greater than 1. + this.limit = 1; + // The selected image mode. Must match with one of the elements in supportedImageModes array. + this.mode = null; +}; + +/** + * Encapsulates all video capture operation configuration options. + */ +var CaptureVideoOptions = function(){ + // Upper limit of videos user can record. Value must be equal or greater than 1. + this.limit = 1; + // Maximum duration of a single video clip in seconds. + this.duration = 0; + // The selected video mode. Must match with one of the elements in supportedVideoModes array. + this.mode = null; +}; + +/** + * Encapsulates all audio capture operation configuration options. + */ +var CaptureAudioOptions = function(){ + // Upper limit of sound clips user can record. Value must be equal or greater than 1. + this.limit = 1; + // Maximum duration of a single sound clip in seconds. + this.duration = 0; + // The selected audio mode. Must match with one of the elements in supportedAudioModes array. + this.mode = null; +}; + +PhoneGap.addConstructor(function(){ + if (typeof navigator.device === "undefined") { + navigator.device = window.device = new Device(); + } + if (typeof navigator.device.capture === "undefined") { + navigator.device.capture = window.device.capture = new Capture(); + } +}); +} + +/* + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2005-2010, Nitobi Software Inc. + * Copyright (c) 2010-2011, IBM Corporation + */ + +if (!PhoneGap.hasResource("compass")) { +PhoneGap.addResource("compass"); + +/** + * This class provides access to device Compass data. + * @constructor + */ +var Compass = function() { + /** + * The last known Compass position. + */ + this.lastHeading = null; + + /** + * List of compass watch timers + */ + this.timers = {}; +}; + +Compass.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"]; + +/** + * Asynchronously aquires the current heading. + * + * @param {Function} successCallback The function to call when the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + * @param {PositionOptions} options The options for getting the heading data such as timeout. (OPTIONAL) + */ +Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, options) { + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + // Get heading + PhoneGap.exec(successCallback, errorCallback, "Compass", "getHeading", []); +}; + +/** + * Asynchronously aquires the heading repeatedly at a given interval. + * + * @param {Function} successCallback The function to call each time the heading data is available + * @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL) + * @param {HeadingOptions} options The options for getting the heading data such as timeout and the frequency of the watch. (OPTIONAL) + * @return String The watch id that must be passed to #clearWatch to stop watching. + */ +Compass.prototype.watchHeading= function(successCallback, errorCallback, options) { + + // Default interval (100 msec) + var frequency = (options !== undefined) ? options.frequency : 100; + + // successCallback required + if (typeof successCallback !== "function") { + console.log("Compass Error: successCallback is not a function"); + return; + } + + // errorCallback optional + if (errorCallback && (typeof errorCallback !== "function")) { + console.log("Compass Error: errorCallback is not a function"); + return; + } + + // Make sure compass timeout > frequency + 10 sec + PhoneGap.exec( + function(timeout) { + if (timeout < (frequency + 10000)) { + PhoneGap.exec(null, null, "Compass", "setTimeout", [frequency + 10000]); + } + }, + function(e) { }, "Compass", "getTimeout", []); + + // Start watch timer to get headings + var id = PhoneGap.createUUID(); + navigator.compass.timers[id] = setInterval( + function() { + PhoneGap.exec(successCallback, errorCallback, "Compass", "getHeading", []); + }, (frequency ? frequency : 1)); + + return id; +}; + + +/** + * Clears the specified heading watch. + * + * @param {String} id The ID of the watch returned from #watchHeading. + */ +Compass.prototype.clearWatch = function(id) { + + // Stop javascript timer & remove from timer list + if (id && navigator.compass.timers[id]) { + clearInterval(navigator.compass.timers[id]); + delete navigator.compass.timers[id]; + } +}; + +PhoneGap.addConstructor(function() { + if (typeof navigator.compass === "undefined") { + navigator.compass = new Compass(); + } +}); +} + + +/* + * PhoneGap is available under *either* the terms of the modified BSD license *or* the + * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. + * + * Copyright (c) 2005-2010, Nitobi Software Inc. + * Copyright (c) 2010-2011, IBM Corporation + */ + +if (!PhoneGap.hasResource("contact")) { +PhoneGap.addResource("contact"); + +/** +* Contains information about a single contact. +* @constructor +* @param {DOMString} id unique identifier +* @param {DOMString} displayName +* @param {ContactName} name +* @param {DOMString} nickname +* @param {Array.} phoneNumbers array of phone numbers +* @param {Array.} emails array of email addresses +* @param {Array.} addresses array of addresses +* @param {Array.} ims instant messaging user ids +* @param {Array.} organizations +* @param {DOMString} birthday contact's birthday +* @param {DOMString} note user notes about contact +* @param {Array.} photos +* @param {Array.} categories +* @param {Array.} urls contact's web sites +*/ +var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses, + ims, organizations, birthday, note, photos, categories, urls) { + this.id = id || null; + this.rawId = null; + this.displayName = displayName || null; + this.name = name || null; // ContactName + this.nickname = nickname || null; + this.phoneNumbers = phoneNumbers || null; // ContactField[] + this.emails = emails || null; // ContactField[] + this.addresses = addresses || null; // ContactAddress[] + this.ims = ims || null; // ContactField[] + this.organizations = organizations || null; // ContactOrganization[] + this.birthday = birthday || null; + this.note = note || null; + this.photos = photos || null; // ContactField[] + this.categories = categories || null; // ContactField[] + this.urls = urls || null; // ContactField[] +}; + +/** + * ContactError. + * An error code assigned by an implementation when an error has occurreds + * @constructor + */ +var ContactError = function() { + this.code=null; +}; + +/** + * Error codes + */ +ContactError.UNKNOWN_ERROR = 0; +ContactError.INVALID_ARGUMENT_ERROR = 1; +ContactError.TIMEOUT_ERROR = 2; +ContactError.PENDING_OPERATION_ERROR = 3; +ContactError.IO_ERROR = 4; +ContactError.NOT_SUPPORTED_ERROR = 5; +ContactError.PERMISSION_DENIED_ERROR = 20; + +/** +* Removes contact from device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.remove = function(successCB, errorCB) { + if (this.id === null) { + var errorObj = new ContactError(); + errorObj.code = ContactError.UNKNOWN_ERROR; + errorCB(errorObj); + } + else { + PhoneGap.exec(successCB, errorCB, "Contacts", "remove", [this.id]); + } +}; + +/** +* Creates a deep copy of this Contact. +* With the contact ID set to null. +* @return copy of this Contact +*/ +Contact.prototype.clone = function() { + var clonedContact = PhoneGap.clone(this); + var i; + clonedContact.id = null; + clonedContact.rawId = null; + // Loop through and clear out any id's in phones, emails, etc. + if (clonedContact.phoneNumbers) { + for (i = 0; i < clonedContact.phoneNumbers.length; i++) { + clonedContact.phoneNumbers[i].id = null; + } + } + if (clonedContact.emails) { + for (i = 0; i < clonedContact.emails.length; i++) { + clonedContact.emails[i].id = null; + } + } + if (clonedContact.addresses) { + for (i = 0; i < clonedContact.addresses.length; i++) { + clonedContact.addresses[i].id = null; + } + } + if (clonedContact.ims) { + for (i = 0; i < clonedContact.ims.length; i++) { + clonedContact.ims[i].id = null; + } + } + if (clonedContact.organizations) { + for (i = 0; i < clonedContact.organizations.length; i++) { + clonedContact.organizations[i].id = null; + } + } + if (clonedContact.tags) { + for (i = 0; i < clonedContact.tags.length; i++) { + clonedContact.tags[i].id = null; + } + } + if (clonedContact.photos) { + for (i = 0; i < clonedContact.photos.length; i++) { + clonedContact.photos[i].id = null; + } + } + if (clonedContact.urls) { + for (i = 0; i < clonedContact.urls.length; i++) { + clonedContact.urls[i].id = null; + } + } + return clonedContact; +}; + +/** +* Persists contact to device storage. +* @param successCB success callback +* @param errorCB error callback +*/ +Contact.prototype.save = function(successCB, errorCB) { + PhoneGap.exec(successCB, errorCB, "Contacts", "save", [this]); +}; + +/** +* Contact name. +* @constructor +* @param formatted +* @param familyName +* @param givenName +* @param middle +* @param prefix +* @param suffix +*/ +var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) { + this.formatted = formatted || null; + this.familyName = familyName || null; + this.givenName = givenName || null; + this.middleName = middle || null; + this.honorificPrefix = prefix || null; + this.honorificSuffix = suffix || null; +}; + +/** +* Generic contact field. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param type +* @param value +* @param pref +*/ +var ContactField = function(type, value, pref) { + this.id = null; + this.type = type || null; + this.value = value || null; + this.pref = pref || null; +}; + +/** +* Contact address. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param formatted +* @param streetAddress +* @param locality +* @param region +* @param postalCode +* @param country +*/ +var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) { + this.id = null; + this.pref = pref || null; + this.type = type || null; + this.formatted = formatted || null; + this.streetAddress = streetAddress || null; + this.locality = locality || null; + this.region = region || null; + this.postalCode = postalCode || null; + this.country = country || null; +}; + +/** +* Contact organization. +* @constructor +* @param {DOMString} id unique identifier, should only be set by native code +* @param name +* @param dept +* @param title +* @param startDate +* @param endDate +* @param location +* @param desc +*/ +var ContactOrganization = function(pref, type, name, dept, title) { + this.id = null; + this.pref = pref || null; + this.type = type || null; + this.name = name || null; + this.department = dept || null; + this.title = title || null; +}; + +/** +* Represents a group of Contacts. +* @constructor +*/ +var Contacts = function() { + this.inProgress = false; + this.records = []; +}; +/** +* Returns an array of Contacts matching the search criteria. +* @param fields that should be searched +* @param successCB success callback +* @param errorCB error callback +* @param {ContactFindOptions} options that can be applied to contact searching +* @return array of Contacts matching search criteria +*/ +Contacts.prototype.find = function(fields, successCB, errorCB, options) { + if (successCB === null) { + throw new TypeError("You must specify a success callback for the find command."); + } + if (fields === null || fields === "undefined" || fields.length === "undefined" || fields.length <= 0) { + if (typeof errorCB === "function") { + errorCB({"code": ContactError.INVALID_ARGUMENT_ERROR}); + } + } else { + PhoneGap.exec(successCB, errorCB, "Contacts", "search", [fields, options]); + } +}; + +/** +* This function creates a new contact, but it does not persist the contact +* to device storage. To persist the contact to device storage, invoke +* contact.save(). +* @param properties an object who's properties will be examined to create a new Contact +* @returns new Contact object +*/ +Contacts.prototype.create = function(properties) { + var i; + var contact = new Contact(); + for (i in properties) { + if (contact[i] !== 'undefined') { + contact[i] = properties[i]; + } + } + return contact; +}; + +/** +* This function returns and array of contacts. It is required as we need to convert raw +* JSON objects into concrete Contact objects. Currently this method is called after +* navigator.contacts.find but before the find methods success call back. +* +* @param jsonArray an array of JSON Objects that need to be converted to Contact objects. +* @returns an array of Contact objects +*/ +Contacts.prototype.cast = function(pluginResult) { + var contacts = []; + var i; + for (i=0; i][;base64], + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsDataURL = function(file) { + this.fileName = ""; + if (typeof file.fullPath === "undefined") { + this.fileName = file; + } else { + this.fileName = file.fullPath; + } + + // LOADING state + this.readyState = FileReader.LOADING; + + // If loadstart callback + if (typeof this.onloadstart === "function") { + this.onloadstart({"type":"loadstart", "target":this}); + } + + var me = this; + + // Read file + navigator.fileMgr.readAsDataURL(this.fileName, + + // Success callback + function(r) { + var evt; + + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // Save result + me.result = r; + + // If onload callback + if (typeof me.onload === "function") { + me.onload({"type":"load", "target":me}); + } + + // DONE state + me.readyState = FileReader.DONE; + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend({"type":"loadend", "target":me}); + } + }, + + // Error callback + function(e) { + var evt; + // If DONE (cancelled), then don't do anything + if (me.readyState === FileReader.DONE) { + return; + } + + // Save error + me.error = e; + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror({"type":"error", "target":me}); + } + + // DONE state + me.readyState = FileReader.DONE; + + // If onloadend callback + if (typeof me.onloadend === "function") { + me.onloadend({"type":"loadend", "target":me}); + } + } + ); +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsBinaryString = function(file) { + // TODO - Can't return binary data to browser. + this.fileName = file; +}; + +/** + * Read file and return data as a binary data. + * + * @param file {File} File object containing file properties + */ +FileReader.prototype.readAsArrayBuffer = function(file) { + // TODO - Can't return binary data to browser. + this.fileName = file; +}; + +//----------------------------------------------------------------------------- +// File Writer +//----------------------------------------------------------------------------- + +/** + * This class writes to the mobile device file system. + * + * For Android: + * The root directory is the root of the file system. + * To write to the SD card, the file name is "sdcard/my_file.txt" + * + * @constructor + * @param file {File} File object containing file properties + * @param append if true write to the end of the file, otherwise overwrite the file + */ +var FileWriter = function(file) { + this.fileName = ""; + this.length = 0; + if (file) { + this.fileName = file.fullPath || file; + this.length = file.size || 0; + } + // default is to write at the beginning of the file + this.position = 0; + + this.readyState = 0; // EMPTY + + this.result = null; + + // Error + this.error = null; + + // Event handlers + this.onwritestart = null; // When writing starts + this.onprogress = null; // While writing the file, and reporting partial file data + this.onwrite = null; // When the write has successfully completed. + this.onwriteend = null; // When the request has completed (either in success or failure). + this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method. + this.onerror = null; // When the write has failed (see errors). +}; + +// States +FileWriter.INIT = 0; +FileWriter.WRITING = 1; +FileWriter.DONE = 2; + +/** + * Abort writing file. + */ +FileWriter.prototype.abort = function() { + // check for invalid state + if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) { + throw FileError.INVALID_STATE_ERR; + } + + // set error + var error = new FileError(), evt; + error.code = error.ABORT_ERR; + this.error = error; + + // If error callback + if (typeof this.onerror === "function") { + this.onerror({"type":"error", "target":this}); + } + // If abort callback + if (typeof this.onabort === "function") { + this.onabort({"type":"abort", "target":this}); + } + + this.readyState = FileWriter.DONE; + + // If write end callback + if (typeof this.onwriteend == "function") { + this.onwriteend({"type":"writeend", "target":this}); + } +}; + +/** + * Writes data to the file + * + * @param text to be written + */ +FileWriter.prototype.write = function(text) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw FileError.INVALID_STATE_ERR; + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart({"type":"writestart", "target":me}); + } + + // Write file + navigator.fileMgr.write(this.fileName, text, this.position, + + // Success callback + function(r) { + var evt; + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // position always increases by bytes written because file would be extended + me.position += r; + // The length of the file is now where we are done writing. + me.length = me.position; + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite({"type":"write", "target":me}); + } + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend({"type":"writeend", "target":me}); + } + }, + + // Error callback + function(e) { + var evt; + + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // Save error + me.error = e; + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror({"type":"error", "target":me}); + } + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend({"type":"writeend", "target":me}); + } + } + ); + +}; + +/** + * Moves the file pointer to the location specified. + * + * If the offset is a negative number the position of the file + * pointer is rewound. If the offset is greater than the file + * size the position is set to the end of the file. + * + * @param offset is the location to move the file pointer to. + */ +FileWriter.prototype.seek = function(offset) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw FileError.INVALID_STATE_ERR; + } + + if (!offset) { + return; + } + + // See back from end of file. + if (offset < 0) { + this.position = Math.max(offset + this.length, 0); + } + // Offset is bigger then file size so set position + // to the end of the file. + else if (offset > this.length) { + this.position = this.length; + } + // Offset is between 0 and file size so set the position + // to start writing. + else { + this.position = offset; + } +}; + +/** + * Truncates the file to the size specified. + * + * @param size to chop the file at. + */ +FileWriter.prototype.truncate = function(size) { + // Throw an exception if we are already writing a file + if (this.readyState === FileWriter.WRITING) { + throw FileError.INVALID_STATE_ERR; + } + + // WRITING state + this.readyState = FileWriter.WRITING; + + var me = this; + + // If onwritestart callback + if (typeof me.onwritestart === "function") { + me.onwritestart({"type":"writestart", "target":this}); + } + + // Write file + navigator.fileMgr.truncate(this.fileName, size, + + // Success callback + function(r) { + var evt; + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // Update the length of the file + me.length = r; + me.position = Math.min(me.position, r); + + // If onwrite callback + if (typeof me.onwrite === "function") { + me.onwrite({"type":"write", "target":me}); + } + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend({"type":"writeend", "target":me}); + } + }, + + // Error callback + function(e) { + var evt; + // If DONE (cancelled), then don't do anything + if (me.readyState === FileWriter.DONE) { + return; + } + + // Save error + me.error = e; + + // If onerror callback + if (typeof me.onerror === "function") { + me.onerror({"type":"error", "target":me}); + } + + // DONE state + me.readyState = FileWriter.DONE; + + // If onwriteend callback + if (typeof me.onwriteend === "function") { + me.onwriteend({"type":"writeend", "target":me}); + } + } + ); +}; + +/** + * Information about the state of the file or directory + * + * @constructor + * {Date} modificationTime (readonly) + */ +var Metadata = function() { + this.modificationTime=null; +}; + +/** + * Supplies arguments to methods that lookup or create files and directories + * + * @constructor + * @param {boolean} create file or directory if it doesn't exist + * @param {boolean} exclusive if true the command will fail if the file or directory exists + */ +var Flags = function(create, exclusive) { + this.create = create || false; + this.exclusive = exclusive || false; +}; + +/** + * An interface representing a file system + * + * @constructor + * {DOMString} name the unique name of the file system (readonly) + * {DirectoryEntry} root directory of the file system (readonly) + */ +var FileSystem = function() { + this.name = null; + this.root = null; +}; + +/** + * An interface that lists the files and directories in a directory. + * @constructor + */ +var DirectoryReader = function(fullPath){ + this.fullPath = fullPath || null; +}; + +/** + * Returns a list of entries from a directory. + * + * @param {Function} successCallback is called with a list of entries + * @param {Function} errorCallback is called with a FileError + */ +DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "readEntries", [this.fullPath]); +}; + +/** + * An interface representing a directory on the file system. + * + * @constructor + * {boolean} isFile always false (readonly) + * {boolean} isDirectory always true (readonly) + * {DOMString} name of the directory, excluding the path leading to it (readonly) + * {DOMString} fullPath the absolute full path to the directory (readonly) + * {FileSystem} filesystem on which the directory resides (readonly) + */ +var DirectoryEntry = function() { + this.isFile = false; + this.isDirectory = true; + this.name = null; + this.fullPath = null; + this.filesystem = null; +}; + +/** + * Copies a directory to a new location + * + * @param {DirectoryEntry} parent the directory to which to copy the entry + * @param {DOMString} newName the new name of the entry, defaults to the current name + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "copyTo", [this.fullPath, parent, newName]); +}; + +/** + * Looks up the metadata of the entry + * + * @param {Function} successCallback is called with a Metadata object + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getMetadata = function(successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "getMetadata", [this.fullPath]); +}; + +/** + * Gets the parent of the entry + * + * @param {Function} successCallback is called with a parent entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getParent = function(successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "getParent", [this.fullPath]); +}; + +/** + * Moves a directory to a new location + * + * @param {DirectoryEntry} parent the directory to which to move the entry + * @param {DOMString} newName the new name of the entry, defaults to the current name + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "moveTo", [this.fullPath, parent, newName]); +}; + +/** + * Removes the entry + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.remove = function(successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "remove", [this.fullPath]); +}; + +/** + * Returns a URI that can be used to identify this entry. + * + * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI. + * @return uri + */ +DirectoryEntry.prototype.toURI = function(mimeType) { + return "file://" + this.fullPath; +}; + +/** + * Creates a new DirectoryReader to read entries from this directory + */ +DirectoryEntry.prototype.createReader = function(successCallback, errorCallback) { + return new DirectoryReader(this.fullPath); +}; + +/** + * Creates or looks up a directory + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory + * @param {Flags} options to create or excluively create the directory + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "getDirectory", [this.fullPath, path, options]); +}; + +/** + * Creates or looks up a file + * + * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file + * @param {Flags} options to create or excluively create the file + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "getFile", [this.fullPath, path, options]); +}; + +/** + * Deletes a directory and all of it's contents + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "removeRecursively", [this.fullPath]); +}; + +/** + * An interface representing a directory on the file system. + * + * @constructor + * {boolean} isFile always true (readonly) + * {boolean} isDirectory always false (readonly) + * {DOMString} name of the file, excluding the path leading to it (readonly) + * {DOMString} fullPath the absolute full path to the file (readonly) + * {FileSystem} filesystem on which the directory resides (readonly) + */ +var FileEntry = function() { + this.isFile = true; + this.isDirectory = false; + this.name = null; + this.fullPath = null; + this.filesystem = null; +}; + +/** + * Copies a file to a new location + * + * @param {DirectoryEntry} parent the directory to which to copy the entry + * @param {DOMString} newName the new name of the entry, defaults to the current name + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +FileEntry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "copyTo", [this.fullPath, parent, newName]); +}; + +/** + * Looks up the metadata of the entry + * + * @param {Function} successCallback is called with a Metadata object + * @param {Function} errorCallback is called with a FileError + */ +FileEntry.prototype.getMetadata = function(successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "getMetadata", [this.fullPath]); +}; + +/** + * Gets the parent of the entry + * + * @param {Function} successCallback is called with a parent entry + * @param {Function} errorCallback is called with a FileError + */ +FileEntry.prototype.getParent = function(successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "getParent", [this.fullPath]); +}; + +/** + * Moves a directory to a new location + * + * @param {DirectoryEntry} parent the directory to which to move the entry + * @param {DOMString} newName the new name of the entry, defaults to the current name + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +FileEntry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "moveTo", [this.fullPath, parent, newName]); +}; + +/** + * Removes the entry + * + * @param {Function} successCallback is called with no parameters + * @param {Function} errorCallback is called with a FileError + */ +FileEntry.prototype.remove = function(successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "remove", [this.fullPath]); +}; + +/** + * Returns a URI that can be used to identify this entry. + * + * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI. + * @return uri + */ +FileEntry.prototype.toURI = function(mimeType) { + return "file://" + this.fullPath; +}; + +/** + * Creates a new FileWriter associated with the file that this FileEntry represents. + * + * @param {Function} successCallback is called with the new FileWriter + * @param {Function} errorCallback is called with a FileError + */ +FileEntry.prototype.createWriter = function(successCallback, errorCallback) { + this.file(function(filePointer) { + var writer = new FileWriter(filePointer); + + if (writer.fileName === null || writer.fileName === "") { + if (typeof errorCallback == "function") { + errorCallback({ + "code": FileError.INVALID_STATE_ERR + }); + } + } + + if (typeof successCallback == "function") { + successCallback(writer); + } + }, errorCallback); +}; + +/** + * Returns a File that represents the current state of the file that this FileEntry represents. + * + * @param {Function} successCallback is called with the new File object + * @param {Function} errorCallback is called with a FileError + */ +FileEntry.prototype.file = function(successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "getFileMetadata", [this.fullPath]); +}; + +/** @constructor */ +var LocalFileSystem = function() { +}; + +// File error codes +LocalFileSystem.TEMPORARY = 0; +LocalFileSystem.PERSISTENT = 1; +LocalFileSystem.RESOURCE = 2; +LocalFileSystem.APPLICATION = 3; + +/** + * Requests a filesystem in which to store application data. + * + * @param {int} type of file system being requested + * @param {Function} successCallback is called with the new FileSystem + * @param {Function} errorCallback is called with a FileError + */ +LocalFileSystem.prototype.requestFileSystem = function(type, size, successCallback, errorCallback) { + if (type < 0 || type > 3) { + if (typeof errorCallback == "function") { + errorCallback({ + "code": FileError.SYNTAX_ERR + }); + } + } + else { + PhoneGap.exec(successCallback, errorCallback, "File", "requestFileSystem", [type, size]); + } +}; + +/** + * + * @param {DOMString} uri referring to a local file in a filesystem + * @param {Function} successCallback is called with the new entry + * @param {Function} errorCallback is called with a FileError + */ +LocalFileSystem.prototype.resolveLocalFileSystemURI = function(uri, successCallback, errorCallback) { + PhoneGap.exec(successCallback, errorCallback, "File", "resolveLocalFileSystemURI", [uri]); +}; + +/** +* This function returns and array of contacts. It is required as we need to convert raw +* JSON objects into concrete Contact objects. Currently this method is called after +* navigator.service.contacts.find but before the find methods success call back. +* +* @param a JSON Objects that need to be converted to DirectoryEntry or FileEntry objects. +* @returns an entry +*/ +LocalFileSystem.prototype._castFS = function(pluginResult) { + var entry = null; + entry = new DirectoryEntry(); + entry.isDirectory = pluginResult.message.root.isDirectory; + entry.isFile = pluginResult.message.root.isFile; + entry.name = pluginResult.message.root.name; + entry.fullPath = pluginResult.message.root.fullPath; + pluginResult.message.root = entry; + return pluginResult; +}; + +LocalFileSystem.prototype._castEntry = function(pluginResult) { + var entry = null; + if (pluginResult.message.isDirectory) { + console.log("This is a dir"); + entry = new DirectoryEntry(); + } + else if (pluginResult.message.isFile) { + console.log("This is a file"); + entry = new FileEntry(); + } + entry.isDirectory = pluginResult.message.isDirectory; + entry.isFile = pluginResult.message.isFile; + entry.name = pluginResult.message.name; + entry.fullPath = pluginResult.message.fullPath; + pluginResult.message = entry; + return pluginResult; +}; + +LocalFileSystem.prototype._castEntries = function(pluginResult) { + var entries = pluginResult.message; + var retVal = []; + for (var i=0; i