source: extensions/piwigo_mobile/ios/www/phonegap.js @ 13271

Last change on this file since 13271 was 12411, checked in by patdenice, 12 years ago

Add piwigo mobile for iOS

File size: 122.8 KB
Line 
1/*
2 * PhoneGap v1.1.0 is available under *either* the terms of the modified BSD license *or* the
3 * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
4 *
5 * Copyright (c) 2005-2010, Nitobi Software Inc.
6 * Copyright (c) 2010-2011, IBM Corporation
7 * Copyright (c) 2011, Codevise Solutions Ltd.
8 * Copyright (c) 2011, Proyectos Equis Ka, S.L.
9 *
10 */
11
12if (typeof PhoneGap === "undefined") {
13
14if (typeof(DeviceInfo) !== 'object'){
15    DeviceInfo = {};
16}
17/**
18 * This represents the PhoneGap API itself, and provides a global namespace for accessing
19 * information about the state of PhoneGap.
20 * @class
21 */
22PhoneGap = {
23    // This queue holds the currently executing command and all pending
24    // commands executed with PhoneGap.exec().
25    commandQueue: [],
26    // Indicates if we're currently in the middle of flushing the command
27    // queue on the native side.
28    commandQueueFlushing: false,
29    _constructors: [],
30    documentEventHandler: {},   // Collection of custom document event handlers
31    windowEventHandler: {} 
32};
33
34/**
35 * List of resource files loaded by PhoneGap.
36 * This is used to ensure JS and other files are loaded only once.
37 */
38PhoneGap.resources = {base: true};
39
40/**
41 * Determine if resource has been loaded by PhoneGap
42 *
43 * @param name
44 * @return
45 */
46PhoneGap.hasResource = function(name) {
47    return PhoneGap.resources[name];
48};
49
50/**
51 * Add a resource to list of loaded resources by PhoneGap
52 *
53 * @param name
54 */
55PhoneGap.addResource = function(name) {
56    PhoneGap.resources[name] = true;
57};
58
59/**
60 * Boolean flag indicating if the PhoneGap API is available and initialized.
61 */ // TODO: Remove this, it is unused here ... -jm
62PhoneGap.available = DeviceInfo.uuid != undefined;
63
64/**
65 * Add an initialization function to a queue that ensures it will run and initialize
66 * application constructors only once PhoneGap has been initialized.
67 * @param {Function} func The function callback you want run once PhoneGap is initialized
68 */
69PhoneGap.addConstructor = function(func) {
70    var state = document.readyState;
71    if ( ( state == 'loaded' || state == 'complete' ) && DeviceInfo.uuid != null )
72    {
73        func();
74    }
75    else
76    {
77        PhoneGap._constructors.push(func);
78    }
79};
80
81(function() 
82 {
83    var timer = setInterval(function()
84    {
85                           
86        var state = document.readyState;
87                           
88        if ( ( state == 'loaded' || state == 'complete' ) && DeviceInfo.uuid != null )
89        {
90            clearInterval(timer); // stop looking
91            // run our constructors list
92            while (PhoneGap._constructors.length > 0) 
93            {
94                var constructor = PhoneGap._constructors.shift();
95                try 
96                {
97                    constructor();
98                } 
99                catch(e) 
100                {
101                    if (typeof(console['log']) == 'function')
102                    {
103                        console.log("Failed to run constructor: " + console.processMessage(e));
104                    }
105                    else
106                    {
107                        alert("Failed to run constructor: " + e.message);
108                    }
109                }
110            }
111            // all constructors run, now fire the deviceready event
112            var e = document.createEvent('Events'); 
113            e.initEvent('deviceready');
114            document.dispatchEvent(e);
115        }
116    }, 1);
117})();
118
119// session id for calls
120PhoneGap.sessionKey = 0;
121
122// centralized callbacks
123PhoneGap.callbackId = 0;
124PhoneGap.callbacks = {};
125PhoneGap.callbackStatus = {
126    NO_RESULT: 0,
127    OK: 1,
128    CLASS_NOT_FOUND_EXCEPTION: 2,
129    ILLEGAL_ACCESS_EXCEPTION: 3,
130    INSTANTIATION_EXCEPTION: 4,
131    MALFORMED_URL_EXCEPTION: 5,
132    IO_EXCEPTION: 6,
133    INVALID_ACTION: 7,
134    JSON_EXCEPTION: 8,
135    ERROR: 9
136    };
137
138/**
139 * Creates a gap bridge iframe used to notify the native code about queued
140 * commands.
141 *
142 * @private
143 */
144PhoneGap.createGapBridge = function() {
145    gapBridge = document.createElement("iframe");
146    gapBridge.setAttribute("style", "display:none;");
147    gapBridge.setAttribute("height","0px");
148    gapBridge.setAttribute("width","0px");
149    gapBridge.setAttribute("frameborder","0");
150    document.documentElement.appendChild(gapBridge);
151    return gapBridge;
152}
153
154/**
155 * Execute a PhoneGap command by queuing it and letting the native side know
156 * there are queued commands. The native side will then request all of the
157 * queued commands and execute them.
158 *
159 * Arguments may be in one of two formats:
160 *
161 * FORMAT ONE (preferable)
162 * The native side will call PhoneGap.callbackSuccess or
163 * PhoneGap.callbackError, depending upon the result of the action.
164 *
165 * @param {Function} success    The success callback
166 * @param {Function} fail       The fail callback
167 * @param {String} service      The name of the service to use
168 * @param {String} action       The name of the action to use
169 * @param {String[]} [args]     Zero or more arguments to pass to the method
170 *     
171 * FORMAT TWO
172 * @param {String} command    Command to be run in PhoneGap, e.g.
173 *                            "ClassName.method"
174 * @param {String[]} [args]   Zero or more arguments to pass to the method
175 *                            object parameters are passed as an array object
176 *                            [object1, object2] each object will be passed as
177 *                            JSON strings
178 */
179PhoneGap.exec = function() { 
180    if (!PhoneGap.available) {
181        alert("ERROR: Attempting to call PhoneGap.exec()"
182              +" before 'deviceready'. Ignoring.");
183        return;
184    }
185
186    var successCallback, failCallback, service, action, actionArgs;
187    var callbackId = null;
188    if (typeof arguments[0] !== "string") {
189        // FORMAT ONE
190        successCallback = arguments[0];
191        failCallback = arguments[1];
192        service = arguments[2];
193        action = arguments[3];
194        actionArgs = arguments[4];
195
196        // Since we need to maintain backwards compatibility, we have to pass
197        // an invalid callbackId even if no callback was provided since plugins
198        // will be expecting it. The PhoneGap.exec() implementation allocates
199        // an invalid callbackId and passes it even if no callbacks were given.
200        callbackId = 'INVALID';
201    } else {
202        // FORMAT TWO
203        splitCommand = arguments[0].split(".");
204        action = splitCommand.pop();
205        service = splitCommand.join(".");
206        actionArgs = Array.prototype.splice.call(arguments, 1);
207    }
208   
209    // Start building the command object.
210    var command = {
211        className: service,
212        methodName: action,
213        arguments: []
214    };
215
216    // Register the callbacks and add the callbackId to the positional
217    // arguments if given.
218    if (successCallback || failCallback) {
219        callbackId = service + PhoneGap.callbackId++;
220        PhoneGap.callbacks[callbackId] = 
221            {success:successCallback, fail:failCallback};
222    }
223    if (callbackId != null) {
224        command.arguments.push(callbackId);
225    }
226
227    for (var i = 0; i < actionArgs.length; ++i) {
228        var arg = actionArgs[i];
229        if (arg == undefined || arg == null) {
230            continue;
231        } else if (typeof(arg) == 'object') {
232            command.options = arg;
233        } else {
234            command.arguments.push(arg);
235        }
236    }
237
238    // Stringify and queue the command. We stringify to command now to
239    // effectively clone the command arguments in case they are mutated before
240    // the command is executed.
241    PhoneGap.commandQueue.push(JSON.stringify(command));
242
243    // If the queue length is 1, then that means it was empty before we queued
244    // the given command, so let the native side know that we have some
245    // commands to execute, unless the queue is currently being flushed, in
246    // which case the command will be picked up without notification.
247    if (PhoneGap.commandQueue.length == 1 && !PhoneGap.commandQueueFlushing) {
248        if (!PhoneGap.gapBridge) {
249            PhoneGap.gapBridge = PhoneGap.createGapBridge();
250        }
251
252        PhoneGap.gapBridge.src = "gap://ready";
253    }
254}
255
256/**
257 * Called by native code to retrieve all queued commands and clear the queue.
258 */
259PhoneGap.getAndClearQueuedCommands = function() {
260  json = JSON.stringify(PhoneGap.commandQueue);
261  PhoneGap.commandQueue = [];
262  return json;
263}
264
265/**
266 * Called by native code when returning successful result from an action.
267 *
268 * @param callbackId
269 * @param args
270 *        args.status - PhoneGap.callbackStatus
271 *        args.message - return value
272 *        args.keepCallback - 0 to remove callback, 1 to keep callback in PhoneGap.callbacks[]
273 */
274PhoneGap.callbackSuccess = function(callbackId, args) {
275    if (PhoneGap.callbacks[callbackId]) {
276
277        // If result is to be sent to callback
278        if (args.status == PhoneGap.callbackStatus.OK) {
279            try {
280                if (PhoneGap.callbacks[callbackId].success) {
281                       PhoneGap.callbacks[callbackId].success(args.message);
282                }
283            }
284            catch (e) {
285                console.log("Error in success callback: "+callbackId+" = "+e);
286            }
287        }
288   
289        // Clear callback if not expecting any more results
290        if (!args.keepCallback) {
291            delete PhoneGap.callbacks[callbackId];
292        }
293    }
294};
295
296/**
297 * Called by native code when returning error result from an action.
298 *
299 * @param callbackId
300 * @param args
301 */
302PhoneGap.callbackError = function(callbackId, args) {
303    if (PhoneGap.callbacks[callbackId]) {
304        try {
305            if (PhoneGap.callbacks[callbackId].fail) {
306                PhoneGap.callbacks[callbackId].fail(args.message);
307            }
308        }
309        catch (e) {
310            console.log("Error in error callback: "+callbackId+" = "+e);
311        }
312       
313        // Clear callback if not expecting any more results
314        if (!args.keepCallback) {
315            delete PhoneGap.callbacks[callbackId];
316        }
317    }
318};
319
320
321/**
322 * Does a deep clone of the object.
323 *
324 * @param obj
325 * @return
326 */
327PhoneGap.clone = function(obj) {
328    if(!obj) { 
329        return obj;
330    }
331
332    if(obj instanceof Array){
333        var retVal = new Array();
334        for(var i = 0; i < obj.length; ++i){
335            retVal.push(PhoneGap.clone(obj[i]));
336        }
337        return retVal;
338    }
339
340    if (obj instanceof Function) {
341        return obj;
342    }
343
344    if(!(obj instanceof Object)){
345        return obj;
346    }
347   
348    if (obj instanceof Date) {
349        return obj;
350    }
351
352    retVal = new Object();
353    for(i in obj){
354        if(!(i in retVal) || retVal[i] != obj[i]) {
355            retVal[i] = PhoneGap.clone(obj[i]);
356        }
357    }
358    return retVal;
359};
360
361// Intercept calls to document.addEventListener
362PhoneGap.m_document_addEventListener = document.addEventListener;
363
364// Intercept calls to window.addEventListener
365PhoneGap.m_window_addEventListener = window.addEventListener;
366
367/**
368 * Add a custom window event handler.
369 *
370 * @param {String} event            The event name that callback handles
371 * @param {Function} callback       The event handler
372 */
373PhoneGap.addWindowEventHandler = function(event, callback) {
374    PhoneGap.windowEventHandler[event] = callback;
375}
376
377/**
378 * Add a custom document event handler.
379 *
380 * @param {String} event            The event name that callback handles
381 * @param {Function} callback       The event handler
382 */
383PhoneGap.addDocumentEventHandler = function(event, callback) {
384    PhoneGap.documentEventHandler[event] = callback;
385}
386
387/**
388 * Intercept adding document event listeners and handle our own
389 *
390 * @param {Object} evt
391 * @param {Function} handler
392 * @param capture
393 */
394document.addEventListener = function(evt, handler, capture) {
395    var e = evt.toLowerCase();
396           
397    // If subscribing to an event that is handled by a plugin
398    if (typeof PhoneGap.documentEventHandler[e] !== "undefined") {
399        if (PhoneGap.documentEventHandler[e](e, handler, true)) {
400            return; // Stop default behavior
401        }
402    }
403   
404    PhoneGap.m_document_addEventListener.call(document, evt, handler, capture); 
405};
406
407/**
408 * Intercept adding window event listeners and handle our own
409 *
410 * @param {Object} evt
411 * @param {Function} handler
412 * @param capture
413 */
414window.addEventListener = function(evt, handler, capture) {
415    var e = evt.toLowerCase();
416       
417    // If subscribing to an event that is handled by a plugin
418    if (typeof PhoneGap.windowEventHandler[e] !== "undefined") {
419        if (PhoneGap.windowEventHandler[e](e, handler, true)) {
420            return; // Stop default behavior
421        }
422    }
423       
424    PhoneGap.m_window_addEventListener.call(window, evt, handler, capture);
425};
426
427// Intercept calls to document.removeEventListener and watch for events that
428// are generated by PhoneGap native code
429PhoneGap.m_document_removeEventListener = document.removeEventListener;
430
431// Intercept calls to window.removeEventListener
432PhoneGap.m_window_removeEventListener = window.removeEventListener;
433
434/**
435 * Intercept removing document event listeners and handle our own
436 *
437 * @param {Object} evt
438 * @param {Function} handler
439 * @param capture
440 */
441document.removeEventListener = function(evt, handler, capture) {
442    var e = evt.toLowerCase();
443
444    // If unsubcribing from an event that is handled by a plugin
445    if (typeof PhoneGap.documentEventHandler[e] !== "undefined") {
446        if (PhoneGap.documentEventHandler[e](e, handler, false)) {
447            return; // Stop default behavior
448        }
449    }
450
451    PhoneGap.m_document_removeEventListener.call(document, evt, handler, capture);
452};
453
454/**
455 * Intercept removing window event listeners and handle our own
456 *
457 * @param {Object} evt
458 * @param {Function} handler
459 * @param capture
460 */
461window.removeEventListener = function(evt, handler, capture) {
462    var e = evt.toLowerCase();
463
464    // If unsubcribing from an event that is handled by a plugin
465    if (typeof PhoneGap.windowEventHandler[e] !== "undefined") {
466        if (PhoneGap.windowEventHandler[e](e, handler, false)) {
467            return; // Stop default behavior
468        }
469    }
470
471    PhoneGap.m_window_removeEventListener.call(window, evt, handler, capture);
472};
473
474/**
475 * Method to fire document event
476 *
477 * @param {String} type             The event type to fire
478 * @param {Object} data             Data to send with event
479 */
480PhoneGap.fireDocumentEvent = function(type, data) {
481    var e = document.createEvent('Events');
482    e.initEvent(type);
483    if (data) {
484        for (var i in data) {
485            e[i] = data[i];
486        }
487    }
488    document.dispatchEvent(e);
489};
490
491/**
492 * Method to fire window event
493 *
494 * @param {String} type             The event type to fire
495 * @param {Object} data             Data to send with event
496 */
497PhoneGap.fireWindowEvent = function(type, data) {
498    var e = document.createEvent('Events');
499    e.initEvent(type);
500    if (data) {
501        for (var i in data) {
502            e[i] = data[i];
503        }
504    }
505    window.dispatchEvent(e);
506};
507
508/**
509 * Method to fire event from native code
510 * Leaving this generic version to handle problems with iOS 3.x. Is currently used by orientation and battery events
511 * Remove when iOS 3.x no longer supported and call fireWindowEvent or fireDocumentEvent directly
512 */
513PhoneGap.fireEvent = function(type, target, data) {
514    var e = document.createEvent('Events');
515    e.initEvent(type);
516    if (data) {
517        for (var i in data) {
518            e[i] = data[i];
519        }
520    }
521    target = target || document;
522    if (target.dispatchEvent === undefined) { // ie window.dispatchEvent is undefined in iOS 3.x
523        target = document;
524    } 
525
526    target.dispatchEvent(e);
527};
528/**
529 * Create a UUID
530 *
531 * @return
532 */
533PhoneGap.createUUID = function() {
534    return PhoneGap.UUIDcreatePart(4) + '-' +
535        PhoneGap.UUIDcreatePart(2) + '-' +
536        PhoneGap.UUIDcreatePart(2) + '-' +
537        PhoneGap.UUIDcreatePart(2) + '-' +
538        PhoneGap.UUIDcreatePart(6);
539};
540
541PhoneGap.UUIDcreatePart = function(length) {
542    var uuidpart = "";
543    for (var i=0; i<length; i++) {
544        var uuidchar = parseInt((Math.random() * 256)).toString(16);
545        if (uuidchar.length == 1) {
546            uuidchar = "0" + uuidchar;
547        }
548        uuidpart += uuidchar;
549    }
550    return uuidpart;
551};
552};
553
554
555if (!PhoneGap.hasResource("debugconsole")) {
556        PhoneGap.addResource("debugconsole");
557       
558/**
559 * This class provides access to the debugging console.
560 * @constructor
561 */
562var DebugConsole = function() {
563    this.winConsole = window.console;
564    this.logLevel = DebugConsole.INFO_LEVEL;
565}
566
567// from most verbose, to least verbose
568DebugConsole.ALL_LEVEL    = 1; // same as first level
569DebugConsole.INFO_LEVEL   = 1;
570DebugConsole.WARN_LEVEL   = 2;
571DebugConsole.ERROR_LEVEL  = 4;
572DebugConsole.NONE_LEVEL   = 8;
573                                                                                                       
574DebugConsole.prototype.setLevel = function(level) {
575    this.logLevel = level;
576};
577
578/**
579 * Utility function for rendering and indenting strings, or serializing
580 * objects to a string capable of being printed to the console.
581 * @param {Object|String} message The string or object to convert to an indented string
582 * @private
583 */
584DebugConsole.prototype.processMessage = function(message, maxDepth) {
585        if (maxDepth === undefined) maxDepth = 0;
586    if (typeof(message) != 'object') {
587        return (this.isDeprecated ? "WARNING: debug object is deprecated, please use console object \n" + message : message);
588    } else {
589        /**
590         * @function
591         * @ignore
592         */
593        function indent(str) {
594            return str.replace(/^/mg, "    ");
595        }
596        /**
597         * @function
598         * @ignore
599         */
600        function makeStructured(obj, depth) {
601            var str = "";
602            for (var i in obj) {
603                try {
604                    if (typeof(obj[i]) == 'object' && depth < maxDepth) {
605                        str += i + ":\n" + indent(makeStructured(obj[i])) + "\n";
606                    } else {
607                        str += i + " = " + indent(String(obj[i])).replace(/^    /, "") + "\n";
608                    }
609                } catch(e) {
610                    str += i + " = EXCEPTION: " + e.message + "\n";
611                }
612            }
613            return str;
614        }
615       
616        return ("Object:\n" + makeStructured(message, maxDepth));
617    }
618};
619
620/**
621 * Print a normal log message to the console
622 * @param {Object|String} message Message or object to print to the console
623 */
624DebugConsole.prototype.log = function(message, maxDepth) {
625    if (PhoneGap.available && this.logLevel <= DebugConsole.INFO_LEVEL)
626        PhoneGap.exec(null, null, 'com.phonegap.debugconsole', 'log',
627            [ this.processMessage(message, maxDepth), { logLevel: 'INFO' } ]
628        );
629    else
630        this.winConsole.log(message);
631};
632
633/**
634 * Print a warning message to the console
635 * @param {Object|String} message Message or object to print to the console
636 */
637DebugConsole.prototype.warn = function(message, maxDepth) {
638    if (PhoneGap.available && this.logLevel <= DebugConsole.WARN_LEVEL)
639        PhoneGap.exec(null, null, 'com.phonegap.debugconsole', 'log',
640            [ this.processMessage(message, maxDepth), { logLevel: 'WARN' } ]
641        );
642    else
643        this.winConsole.error(message);
644};
645
646/**
647 * Print an error message to the console
648 * @param {Object|String} message Message or object to print to the console
649 */
650DebugConsole.prototype.error = function(message, maxDepth) {
651    if (PhoneGap.available && this.logLevel <= DebugConsole.ERROR_LEVEL)
652                PhoneGap.exec(null, null, 'com.phonegap.debugconsole', 'log',
653            [ this.processMessage(message, maxDepth), { logLevel: 'ERROR' } ]
654        );
655    else
656        this.winConsole.error(message);
657};
658
659PhoneGap.addConstructor(function() {
660    window.console = new DebugConsole();
661});
662};
663if (!PhoneGap.hasResource("position")) {
664        PhoneGap.addResource("position");
665
666/**
667 * This class contains position information.
668 * @param {Object} lat
669 * @param {Object} lng
670 * @param {Object} acc
671 * @param {Object} alt
672 * @param {Object} altAcc
673 * @param {Object} head
674 * @param {Object} vel
675 * @constructor
676 */
677Position = function(coords, timestamp) {
678        this.coords = Coordinates.cloneFrom(coords);
679    this.timestamp = timestamp || new Date().getTime();
680};
681
682Position.prototype.equals = function(other) {
683    return (this.coords && other && other.coords &&
684            this.coords.latitude == other.coords.latitude &&
685            this.coords.longitude == other.coords.longitude);
686};
687
688Position.prototype.clone = function()
689{
690    return new Position(
691        this.coords? this.coords.clone() : null,
692        this.timestamp? this.timestamp : new Date().getTime()
693    );
694}
695
696Coordinates = function(lat, lng, alt, acc, head, vel, altAcc) {
697        /**
698         * The latitude of the position.
699         */
700        this.latitude = lat;
701        /**
702         * The longitude of the position,
703         */
704        this.longitude = lng;
705        /**
706         * The altitude of the position.
707         */
708        this.altitude = alt;
709        /**
710         * The accuracy of the position.
711         */
712        this.accuracy = acc;
713        /**
714         * The direction the device is moving at the position.
715         */
716        this.heading = head;
717        /**
718         * The velocity with which the device is moving at the position.
719         */
720        this.speed = vel;
721        /**
722         * The altitude accuracy of the position.
723         */
724        this.altitudeAccuracy = (altAcc != 'undefined') ? altAcc : null; 
725};
726
727Coordinates.prototype.clone = function()
728{
729    return new Coordinates(
730        this.latitude,
731        this.longitude,
732        this.altitude,
733        this.accuracy,
734        this.heading,
735        this.speed,
736        this.altitudeAccuracy
737    );
738};
739
740Coordinates.cloneFrom = function(obj)
741{
742    return new Coordinates(
743        obj.latitude,
744        obj.longitude,
745        obj.altitude,
746        obj.accuracy,
747        obj.heading,
748        obj.speed,
749        obj.altitudeAccuracy
750    );
751};
752
753/**
754 * This class specifies the options for requesting position data.
755 * @constructor
756 */
757PositionOptions = function(enableHighAccuracy, timeout, maximumAge) {
758        /**
759         * Specifies the desired position accuracy.
760         */
761        this.enableHighAccuracy = enableHighAccuracy || false;
762        /**
763         * The timeout after which if position data cannot be obtained the errorCallback
764         * is called.
765         */
766        this.timeout = timeout || 10000;
767        /**
768     * The age of a cached position whose age is no greater than the specified time
769     * in milliseconds.
770     */
771        this.maximumAge = maximumAge || 0;
772       
773        if (this.maximumAge < 0) {
774                this.maximumAge = 0;
775        }
776};
777
778/**
779 * This class contains information about any GPS errors.
780 * @constructor
781 */
782PositionError = function(code, message) {
783        this.code = code || 0;
784        this.message = message || "";
785};
786
787PositionError.UNKNOWN_ERROR = 0;
788PositionError.PERMISSION_DENIED = 1;
789PositionError.POSITION_UNAVAILABLE = 2;
790PositionError.TIMEOUT = 3;
791
792};if (!PhoneGap.hasResource("acceleration")) {
793        PhoneGap.addResource("acceleration");
794       
795
796/**
797 * This class contains acceleration information
798 * @constructor
799 * @param {Number} x The force applied by the device in the x-axis.
800 * @param {Number} y The force applied by the device in the y-axis.
801 * @param {Number} z The force applied by the device in the z-axis.
802 */
803Acceleration = function(x, y, z) {
804        /**
805         * The force applied by the device in the x-axis.
806         */
807        this.x = x;
808        /**
809         * The force applied by the device in the y-axis.
810         */
811        this.y = y;
812        /**
813         * The force applied by the device in the z-axis.
814         */
815        this.z = z;
816        /**
817         * The time that the acceleration was obtained.
818         */
819        this.timestamp = new Date().getTime();
820}
821
822/**
823 * This class specifies the options for requesting acceleration data.
824 * @constructor
825 */
826AccelerationOptions = function() {
827        /**
828         * The timeout after which if acceleration data cannot be obtained the errorCallback
829         * is called.
830         */
831        this.timeout = 10000;
832}
833};if (!PhoneGap.hasResource("accelerometer")) {
834        PhoneGap.addResource("accelerometer");
835
836/**
837 * This class provides access to device accelerometer data.
838 * @constructor
839 */
840Accelerometer = function() 
841{
842        /**
843         * The last known acceleration.
844         */
845        this.lastAcceleration = new Acceleration(0,0,0);
846}
847
848/**
849 * Asynchronously aquires the current acceleration.
850 * @param {Function} successCallback The function to call when the acceleration
851 * data is available
852 * @param {Function} errorCallback The function to call when there is an error
853 * getting the acceleration data.
854 * @param {AccelerationOptions} options The options for getting the accelerometer data
855 * such as timeout.
856 */
857Accelerometer.prototype.getCurrentAcceleration = function(successCallback, errorCallback, options) {
858        // If the acceleration is available then call success
859        // If the acceleration is not available then call error
860       
861        // Created for iPhone, Iphone passes back _accel obj litteral
862        if (typeof successCallback == "function") {
863                successCallback(this.lastAcceleration);
864        }
865};
866
867// private callback called from Obj-C by name
868Accelerometer.prototype._onAccelUpdate = function(x,y,z)
869{
870   this.lastAcceleration = new Acceleration(x,y,z);
871};
872
873/**
874 * Asynchronously aquires the acceleration repeatedly at a given interval.
875 * @param {Function} successCallback The function to call each time the acceleration
876 * data is available
877 * @param {Function} errorCallback The function to call when there is an error
878 * getting the acceleration data.
879 * @param {AccelerationOptions} options The options for getting the accelerometer data
880 * such as timeout.
881 */
882
883Accelerometer.prototype.watchAcceleration = function(successCallback, errorCallback, options) {
884        //this.getCurrentAcceleration(successCallback, errorCallback, options);
885        // TODO: add the interval id to a list so we can clear all watches
886        var frequency = (options != undefined && options.frequency != undefined) ? options.frequency : 10000;
887        var updatedOptions = {
888                desiredFrequency:frequency 
889        }
890        PhoneGap.exec(null, null, "com.phonegap.accelerometer", "start", [options]);
891
892        return setInterval(function() {
893                navigator.accelerometer.getCurrentAcceleration(successCallback, errorCallback, options);
894        }, frequency);
895};
896
897/**
898 * Clears the specified accelerometer watch.
899 * @param {String} watchId The ID of the watch returned from #watchAcceleration.
900 */
901Accelerometer.prototype.clearWatch = function(watchId) {
902        PhoneGap.exec(null, null, "com.phonegap.accelerometer", "stop", []);
903        clearInterval(watchId);
904};
905
906Accelerometer.install = function()
907{
908    if (typeof navigator.accelerometer == "undefined") {
909                navigator.accelerometer = new Accelerometer();
910        }
911};
912
913Accelerometer.installDeviceMotionHandler = function()
914{
915        if (!(window.DeviceMotionEvent == undefined)) {
916                // supported natively, so we don't have to add support
917                return;
918        }       
919       
920        var self = this;
921        var devicemotionEvent = 'devicemotion';
922        self.deviceMotionWatchId = null;
923        self.deviceMotionListenerCount = 0;
924        self.deviceMotionLastEventTimestamp = 0;
925       
926        // backup original `window.addEventListener`, `window.removeEventListener`
927    var _addEventListener = window.addEventListener;
928    var _removeEventListener = window.removeEventListener;
929                                                                                                       
930        var windowDispatchAvailable = !(window.dispatchEvent === undefined); // undefined in iOS 3.x
931                                                                                                       
932        var accelWin = function(acceleration) {
933                var evt = document.createEvent('Events');
934            evt.initEvent(devicemotionEvent);
935       
936                evt.acceleration = null; // not all devices have gyroscope, don't care for now if we actually have it.
937                evt.rotationRate = null; // not all devices have gyroscope, don't care for now if we actually have it:
938                evt.accelerationIncludingGravity = acceleration; // accelerometer, all iOS devices have it
939               
940                var currentTime = new Date().getTime();
941                evt.interval =  (self.deviceMotionLastEventTimestamp == 0) ? 0 : (currentTime - self.deviceMotionLastEventTimestamp);
942                self.deviceMotionLastEventTimestamp = currentTime;
943               
944                if (windowDispatchAvailable) {
945                        window.dispatchEvent(evt);
946                } else {
947                        document.dispatchEvent(evt);
948                }
949        };
950       
951        var accelFail = function() {
952               
953        };
954                                                                                                       
955    // override `window.addEventListener`
956    window.addEventListener = function() {
957        if (arguments[0] === devicemotionEvent) {
958            ++(self.deviceMotionListenerCount);
959                        if (self.deviceMotionListenerCount == 1) { // start
960                                self.deviceMotionWatchId = navigator.accelerometer.watchAcceleration(accelWin, accelFail, { frequency:500});
961                        }
962                } 
963                                                                                                       
964                if (!windowDispatchAvailable) {
965                        return document.addEventListener.apply(this, arguments);
966                } else {
967                        return _addEventListener.apply(this, arguments);
968                }
969    }; 
970
971    // override `window.removeEventListener'
972    window.removeEventListener = function() {
973        if (arguments[0] === devicemotionEvent) {
974            --(self.deviceMotionListenerCount);
975                        if (self.deviceMotionListenerCount == 0) { // stop
976                                navigator.accelerometer.clearWatch(self.deviceMotionWatchId);
977                        }
978                } 
979               
980                if (!windowDispatchAvailable) {
981                        return document.removeEventListener.apply(this, arguments);
982                } else {
983                        return _removeEventListener.apply(this, arguments);
984                }
985    }; 
986};
987
988
989PhoneGap.addConstructor(Accelerometer.install);
990PhoneGap.addConstructor(Accelerometer.installDeviceMotionHandler);
991
992};/*
993 * PhoneGap is available under *either* the terms of the modified BSD license *or* the
994 * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
995 *
996 * Copyright (c) 2005-2010, Nitobi Software Inc.
997 * Copyright (c) 2010-2011, IBM Corporation
998 */
999
1000if (!PhoneGap.hasResource("battery")) {
1001PhoneGap.addResource("battery");
1002
1003/**
1004 * This class contains information about the current battery status.
1005 * @constructor
1006 */
1007var Battery = function() {
1008    this._level = null;
1009    this._isPlugged = null;
1010    this._batteryListener = [];
1011    this._lowListener = [];
1012    this._criticalListener = [];
1013};
1014
1015/**
1016 * Registers as an event producer for battery events.
1017 *
1018 * @param {Object} eventType
1019 * @param {Object} handler
1020 * @param {Object} add
1021 */
1022Battery.prototype.eventHandler = function(eventType, handler, add) {
1023    var me = navigator.battery;
1024    if (add) {
1025        // If there are no current registered event listeners start the battery listener on native side.
1026        if (me._batteryListener.length === 0 && me._lowListener.length === 0 && me._criticalListener.length === 0) {
1027            PhoneGap.exec(me._status, me._error, "com.phonegap.battery", "start", []);
1028        }
1029       
1030        // Register the event listener in the proper array
1031        if (eventType === "batterystatus") {
1032            var pos = me._batteryListener.indexOf(handler);
1033            if (pos === -1) {
1034                me._batteryListener.push(handler);
1035            }
1036        } else if (eventType === "batterylow") {
1037            var pos = me._lowListener.indexOf(handler);
1038            if (pos === -1) {
1039                me._lowListener.push(handler);
1040            }
1041        } else if (eventType === "batterycritical") {
1042            var pos = me._criticalListener.indexOf(handler);
1043            if (pos === -1) {
1044                me._criticalListener.push(handler);
1045            }
1046        }
1047    } else {
1048        // Remove the event listener from the proper array
1049        if (eventType === "batterystatus") {
1050            var pos = me._batteryListener.indexOf(handler);
1051            if (pos > -1) {
1052                me._batteryListener.splice(pos, 1);       
1053            }
1054        } else if (eventType === "batterylow") {
1055            var pos = me._lowListener.indexOf(handler);
1056            if (pos > -1) {
1057                me._lowListener.splice(pos, 1);       
1058            }
1059        } else if (eventType === "batterycritical") {
1060            var pos = me._criticalListener.indexOf(handler);
1061            if (pos > -1) {
1062                me._criticalListener.splice(pos, 1);       
1063            }
1064        }
1065       
1066        // If there are no more registered event listeners stop the battery listener on native side.
1067        if (me._batteryListener.length === 0 && me._lowListener.length === 0 && me._criticalListener.length === 0) {
1068            PhoneGap.exec(null, null, "com.phonegap.battery", "stop", []);
1069        }
1070    }
1071};
1072
1073/**
1074 * Callback for battery status
1075 *
1076 * @param {Object} info                 keys: level, isPlugged
1077 */
1078Battery.prototype._status = function(info) {
1079        if (info) {
1080                var me = this;
1081                if (me._level != info.level || me._isPlugged != info.isPlugged) {
1082                        // Fire batterystatus event
1083                        //PhoneGap.fireWindowEvent("batterystatus", info);
1084                        // use this workaround since iOS 3.x does have window.dispatchEvent
1085                        PhoneGap.fireEvent("batterystatus", window, info);     
1086
1087                        // Fire low battery event
1088                        if (info.level == 20 || info.level == 5) {
1089                                if (info.level == 20) {
1090                                        //PhoneGap.fireWindowEvent("batterylow", info);
1091                                        // use this workaround since iOS 3.x does not have window.dispatchEvent
1092                                        PhoneGap.fireEvent("batterylow", window, info);
1093                                }
1094                                else {
1095                                        //PhoneGap.fireWindowEvent("batterycritical", info);
1096                                        // use this workaround since iOS 3.x does not have window.dispatchEvent
1097                                        PhoneGap.fireEvent("batterycritical", window, info);
1098                                }
1099                        }
1100                }
1101                me._level = info.level;
1102                me._isPlugged = info.isPlugged; 
1103        }
1104};
1105
1106/**
1107 * Error callback for battery start
1108 */
1109Battery.prototype._error = function(e) {
1110    console.log("Error initializing Battery: " + e);
1111};
1112
1113PhoneGap.addConstructor(function() {
1114    if (typeof navigator.battery === "undefined") {
1115        navigator.battery = new Battery();
1116        PhoneGap.addWindowEventHandler("batterystatus", navigator.battery.eventHandler);
1117        PhoneGap.addWindowEventHandler("batterylow", navigator.battery.eventHandler);
1118        PhoneGap.addWindowEventHandler("batterycritical", navigator.battery.eventHandler);
1119    }
1120});
1121}if (!PhoneGap.hasResource("camera")) {
1122        PhoneGap.addResource("camera");
1123       
1124
1125/**
1126 * This class provides access to the device camera.
1127 * @constructor
1128 */
1129Camera = function() {
1130       
1131}
1132/**
1133 *  Available Camera Options
1134 *  {boolean} allowEdit - true to allow editing image, default = false
1135 *      {number} quality 0-100 (low to high) default =  100
1136 *  {Camera.DestinationType} destinationType default = DATA_URL
1137 *      {Camera.PictureSourceType} sourceType default = CAMERA
1138 *      {number} targetWidth - width in pixels to scale image default = 0 (no scaling)
1139 *  {number} targetHeight - height in pixels to scale image default = 0 (no scaling)
1140 *  {Camera.EncodingType} - encodingType default = JPEG
1141 */
1142/**
1143 * Format of image that is returned from getPicture.
1144 *
1145 * Example: navigator.camera.getPicture(success, fail,
1146 *              { quality: 80,
1147 *                destinationType: Camera.DestinationType.DATA_URL,
1148 *                sourceType: Camera.PictureSourceType.PHOTOLIBRARY})
1149 */
1150Camera.DestinationType = {
1151    DATA_URL: 0,                // Return base64 encoded string
1152    FILE_URI: 1                 // Return file uri
1153};
1154Camera.prototype.DestinationType = Camera.DestinationType;
1155
1156/**
1157 * Source to getPicture from.
1158 *
1159 * Example: navigator.camera.getPicture(success, fail,
1160 *              { quality: 80,
1161 *                destinationType: Camera.DestinationType.DATA_URL,
1162 *                sourceType: Camera.PictureSourceType.PHOTOLIBRARY})
1163 */
1164Camera.PictureSourceType = {
1165    PHOTOLIBRARY : 0,           // Choose image from picture library
1166    CAMERA : 1,                 // Take picture from camera
1167    SAVEDPHOTOALBUM : 2         // Choose image from picture library
1168};
1169Camera.prototype.PictureSourceType = Camera.PictureSourceType;
1170
1171/**
1172 * Encoding of image returned from getPicture.
1173 *
1174 * Example: navigator.camera.getPicture(success, fail,
1175 *              { quality: 80,
1176 *                destinationType: Camera.DestinationType.DATA_URL,
1177 *                sourceType: Camera.PictureSourceType.CAMERA,
1178 *                encodingType: Camera.EncodingType.PNG})
1179 */ 
1180Camera.EncodingType = { 
1181        JPEG: 0,                    // Return JPEG encoded image
1182        PNG: 1                      // Return PNG encoded image
1183};
1184Camera.prototype.EncodingType = Camera.EncodingType;
1185
1186/**
1187 * Type of pictures to select from.  Only applicable when
1188 *      PictureSourceType is PHOTOLIBRARY or SAVEDPHOTOALBUM
1189 *
1190 * Example: navigator.camera.getPicture(success, fail,
1191 *              { quality: 80,
1192 *                destinationType: Camera.DestinationType.DATA_URL,
1193 *                sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
1194 *                mediaType: Camera.MediaType.PICTURE})
1195 */ 
1196Camera.MediaType = { 
1197        PICTURE: 0,             // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
1198        VIDEO: 1,                // allow selection of video only, ONLY RETURNS URL
1199        ALLMEDIA : 2                    // allow selection from all media types
1200};
1201Camera.prototype.MediaType = Camera.MediaType;
1202
1203/**
1204 * Gets a picture from source defined by "options.sourceType", and returns the
1205 * image as defined by the "options.destinationType" option.
1206
1207 * The defaults are sourceType=CAMERA and destinationType=DATA_URL.
1208 *
1209 * @param {Function} successCallback
1210 * @param {Function} errorCallback
1211 * @param {Object} options
1212 */
1213Camera.prototype.getPicture = function(successCallback, errorCallback, options) {
1214        // successCallback required
1215        if (typeof successCallback != "function") {
1216        console.log("Camera Error: successCallback is not a function");
1217        return;
1218    }
1219
1220    // errorCallback optional
1221    if (errorCallback && (typeof errorCallback != "function")) {
1222        console.log("Camera Error: errorCallback is not a function");
1223        return;
1224    }
1225       
1226        PhoneGap.exec(successCallback, errorCallback, "com.phonegap.camera","getPicture",[options]);
1227};
1228
1229
1230
1231PhoneGap.addConstructor(function() {
1232    if (typeof navigator.camera == "undefined") navigator.camera = new Camera();
1233});
1234};
1235
1236if (!PhoneGap.hasResource("device")) {
1237        PhoneGap.addResource("device");
1238
1239/**
1240 * this represents the mobile device, and provides properties for inspecting the model, version, UUID of the
1241 * phone, etc.
1242 * @constructor
1243 */
1244Device = function() 
1245{
1246    this.platform = null;
1247    this.version  = null;
1248    this.name     = null;
1249    this.phonegap      = null;
1250    this.uuid     = null;
1251    try 
1252        {     
1253                this.platform = DeviceInfo.platform;
1254                this.version  = DeviceInfo.version;
1255                this.name     = DeviceInfo.name;
1256                this.phonegap = DeviceInfo.gap;
1257                this.uuid     = DeviceInfo.uuid;
1258
1259    } 
1260        catch(e) 
1261        {
1262        // TODO:
1263    }
1264        this.available = PhoneGap.available = this.uuid != null;
1265}
1266
1267PhoneGap.addConstructor(function() {
1268        if (typeof navigator.device === "undefined") {
1269        navigator.device = window.device = new Device();
1270        }
1271});
1272};
1273
1274if (!PhoneGap.hasResource("capture")) {
1275        PhoneGap.addResource("capture");
1276/**
1277 * The CaptureError interface encapsulates all errors in the Capture API.
1278 */
1279function CaptureError() {
1280   this.code = null;
1281};
1282
1283// Capture error codes
1284CaptureError.CAPTURE_INTERNAL_ERR = 0;
1285CaptureError.CAPTURE_APPLICATION_BUSY = 1;
1286CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
1287CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
1288CaptureError.CAPTURE_NOT_SUPPORTED = 20;
1289
1290/**
1291 * The Capture interface exposes an interface to the camera and microphone of the hosting device.
1292 */
1293function Capture() {
1294        this.supportedAudioModes = [];
1295        this.supportedImageModes = [];
1296        this.supportedVideoModes = [];
1297};
1298
1299/**
1300 * Launch audio recorder application for recording audio clip(s).
1301 *
1302 * @param {Function} successCB
1303 * @param {Function} errorCB
1304 * @param {CaptureAudioOptions} options
1305 *
1306 * No audio recorder to launch for iOS - return CAPTURE_NOT_SUPPORTED
1307 */
1308Capture.prototype.captureAudio = function(successCallback, errorCallback, options) {
1309        /*if (errorCallback && typeof errorCallback === "function") {
1310                errorCallback({
1311                                "code": CaptureError.CAPTURE_NOT_SUPPORTED
1312                        });
1313        }*/
1314    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.mediacapture", "captureAudio", [options]);
1315};
1316
1317/**
1318 * Launch camera application for taking image(s).
1319 *
1320 * @param {Function} successCB
1321 * @param {Function} errorCB
1322 * @param {CaptureImageOptions} options
1323 */
1324Capture.prototype.captureImage = function(successCallback, errorCallback, options) {
1325    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.mediacapture", "captureImage", [options]);
1326};
1327
1328/**
1329 * Launch camera application for taking image(s).
1330 *
1331 * @param {Function} successCB
1332 * @param {Function} errorCB
1333 * @param {CaptureImageOptions} options
1334 */
1335Capture.prototype._castMediaFile = function(pluginResult) {
1336    var mediaFiles = [];
1337    var i;
1338    for (i=0; i<pluginResult.message.length; i++) {
1339        var mediaFile = new MediaFile();
1340            mediaFile.name = pluginResult.message[i].name;
1341            mediaFile.fullPath = pluginResult.message[i].fullPath;
1342            mediaFile.type = pluginResult.message[i].type;
1343            mediaFile.lastModifiedDate = pluginResult.message[i].lastModifiedDate;
1344            mediaFile.size = pluginResult.message[i].size;
1345        mediaFiles.push(mediaFile);
1346    }
1347    pluginResult.message = mediaFiles;
1348    return pluginResult;
1349};
1350
1351/**
1352 * Launch device camera application for recording video(s).
1353 *
1354 * @param {Function} successCB
1355 * @param {Function} errorCB
1356 * @param {CaptureVideoOptions} options
1357 */
1358Capture.prototype.captureVideo = function(successCallback, errorCallback, options) {
1359    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.mediacapture", "captureVideo", [options]);
1360};
1361
1362/**
1363 * Encapsulates a set of parameters that the capture device supports.
1364 */
1365function ConfigurationData() {
1366    // The ASCII-encoded string in lower case representing the media type.
1367    this.type; 
1368    // The height attribute represents height of the image or video in pixels.
1369    // In the case of a sound clip this attribute has value 0.
1370    this.height = 0;
1371    // The width attribute represents width of the image or video in pixels.
1372    // In the case of a sound clip this attribute has value 0
1373    this.width = 0;
1374};
1375
1376/**
1377 * Encapsulates all image capture operation configuration options.
1378 */
1379var CaptureImageOptions = function() {
1380    // Upper limit of images user can take. Value must be equal or greater than 1.
1381    this.limit = 1; 
1382    // The selected image mode. Must match with one of the elements in supportedImageModes array.
1383    this.mode = null; 
1384};
1385
1386/**
1387 * Encapsulates all video capture operation configuration options.
1388 */
1389var CaptureVideoOptions = function() {
1390    // Upper limit of videos user can record. Value must be equal or greater than 1.
1391    this.limit = 1;
1392    // Maximum duration of a single video clip in seconds.
1393    this.duration = 0;
1394    // The selected video mode. Must match with one of the elements in supportedVideoModes array.
1395    this.mode = null;
1396};
1397
1398/**
1399 * Encapsulates all audio capture operation configuration options.
1400 */
1401var CaptureAudioOptions = function() {
1402    // Upper limit of sound clips user can record. Value must be equal or greater than 1.
1403    this.limit = 1;
1404    // Maximum duration of a single sound clip in seconds.
1405    this.duration = 0;
1406    // The selected audio mode. Must match with one of the elements in supportedAudioModes array.
1407    this.mode = null;
1408};
1409
1410/**
1411 * Represents a single file.
1412 *
1413 * name {DOMString} name of the file, without path information
1414 * fullPath {DOMString} the full path of the file, including the name
1415 * type {DOMString} mime type
1416 * lastModifiedDate {Date} last modified date
1417 * size {Number} size of the file in bytes
1418 */
1419function MediaFile(name, fullPath, type, lastModifiedDate, size) {
1420    this.name = name || null;
1421    this.fullPath = fullPath || null;
1422    this.type = type || null;
1423    this.lastModifiedDate = lastModifiedDate || null;
1424    this.size = size || 0;
1425}
1426
1427/**
1428 * Request capture format data for a specific file and type
1429 *
1430 * @param {Function} successCB
1431 * @param {Function} errorCB
1432 */
1433MediaFile.prototype.getFormatData = function(successCallback, errorCallback) {
1434        if (typeof this.fullPath === "undefined" || this.fullPath === null) {
1435                errorCallback({
1436                                "code": CaptureError.CAPTURE_INVALID_ARGUMENT
1437                        });
1438        } else {
1439        PhoneGap.exec(successCallback, errorCallback, "com.phonegap.mediacapture", "getFormatData", [this.fullPath, this.type]);
1440        }       
1441};
1442
1443/**
1444 * MediaFileData encapsulates format information of a media file.
1445 *
1446 * @param {DOMString} codecs
1447 * @param {long} bitrate
1448 * @param {long} height
1449 * @param {long} width
1450 * @param {float} duration
1451 */
1452function MediaFileData(codecs, bitrate, height, width, duration) {
1453    this.codecs = codecs || null;
1454    this.bitrate = bitrate || 0;
1455    this.height = height || 0;
1456    this.width = width || 0;
1457    this.duration = duration || 0;
1458}
1459
1460PhoneGap.addConstructor(function() {
1461    if (typeof navigator.device === "undefined") {
1462        navigator.device = window.device = new Device();
1463    }
1464    if (typeof navigator.device.capture === "undefined") {
1465        navigator.device.capture = window.device.capture = new Capture();
1466    }
1467});
1468};
1469if (!PhoneGap.hasResource("contact")) {
1470        PhoneGap.addResource("contact");
1471
1472
1473/**
1474* Contains information about a single contact.
1475* @param {DOMString} id unique identifier
1476* @param {DOMString} displayName
1477* @param {ContactName} name
1478* @param {DOMString} nickname
1479* @param {ContactField[]} phoneNumbers array of phone numbers
1480* @param {ContactField[]} emails array of email addresses
1481* @param {ContactAddress[]} addresses array of addresses
1482* @param {ContactField[]} ims instant messaging user ids
1483* @param {ContactOrganization[]} organizations
1484* @param {DOMString} birthday contact's birthday
1485* @param {DOMString} note user notes about contact
1486* @param {ContactField[]} photos
1487* @param {Array.<ContactField>} categories
1488* @param {ContactField[]} urls contact's web sites
1489*/
1490var Contact = function(id, displayName, name, nickname, phoneNumbers, emails, addresses,
1491    ims, organizations, birthday, note, photos, categories, urls) {
1492    this.id = id || null;
1493    this.displayName = displayName || null;
1494    this.name = name || null; // ContactName
1495    this.nickname = nickname || null;
1496    this.phoneNumbers = phoneNumbers || null; // ContactField[]
1497    this.emails = emails || null; // ContactField[]
1498    this.addresses = addresses || null; // ContactAddress[]
1499    this.ims = ims || null; // ContactField[]
1500    this.organizations = organizations || null; // ContactOrganization[]
1501    this.birthday = birthday || null; // JS Date
1502    this.note = note || null;
1503    this.photos = photos || null; // ContactField[]
1504    this.categories = categories || null; 
1505    this.urls = urls || null; // ContactField[]
1506};
1507
1508/**
1509* Converts Dates to milliseconds before sending to iOS
1510*/
1511Contact.prototype.convertDatesOut = function()
1512{
1513        var dates = new Array("birthday");
1514        for (var i=0; i<dates.length; i++){
1515                var value = this[dates[i]];
1516                if (value){
1517                        if (!value instanceof Date){
1518                                try {
1519                                        value = new Date(value);
1520                                } catch(exception){
1521                                        value = null;
1522                                }
1523                        }
1524                        if (value instanceof Date){
1525                                value = value.valueOf();
1526                        }
1527                        this[dates[i]] = value;
1528                }
1529        }
1530       
1531};
1532/**
1533* Converts milliseconds to JS Date when returning from iOS
1534*/
1535Contact.prototype.convertDatesIn = function()
1536{
1537        var dates = new Array("birthday");
1538        for (var i=0; i<dates.length; i++){
1539                var value = this[dates[i]];
1540                if (value){
1541                        try {
1542                                this[dates[i]] = new Date(parseFloat(value));
1543                        } catch (exception){
1544                                console.log("exception creating date");
1545                        }
1546                }
1547        }
1548};
1549/**
1550* Removes contact from device storage.
1551* @param successCB success callback
1552* @param errorCB error callback (optional)
1553*/
1554Contact.prototype.remove = function(successCB, errorCB) {
1555        if (this.id == null) {
1556        var errorObj = new ContactError();
1557        errorObj.code = ContactError.UNKNOWN_ERROR;
1558        errorCB(errorObj);
1559    }
1560    else {
1561        PhoneGap.exec(successCB, errorCB, "com.phonegap.contacts", "remove", [{ "contact": this}]);
1562    }
1563};
1564/**
1565* iOS ONLY
1566* displays contact via iOS UI
1567*       NOT part of W3C spec so no official documentation
1568*
1569* @param errorCB error callback
1570* @param options object
1571*       allowsEditing: boolean AS STRING
1572*               "true" to allow editing the contact
1573*               "false" (default) display contact
1574*/
1575Contact.prototype.display = function(errorCB, options) { 
1576        if (this.id == null) {
1577        if (typeof errorCB == "function") {
1578                var errorObj = new ContactError();
1579                errorObj.code = ContactError.UNKNOWN_ERROR;
1580                errorCB(errorObj);
1581                }
1582    }
1583    else {
1584        PhoneGap.exec(null, errorCB, "com.phonegap.contacts","displayContact", [this.id, options]);
1585    }
1586};
1587
1588/**
1589* Creates a deep copy of this Contact.
1590* With the contact ID set to null.
1591* @return copy of this Contact
1592*/
1593Contact.prototype.clone = function() {
1594    var clonedContact = PhoneGap.clone(this);
1595    clonedContact.id = null;
1596    // Loop through and clear out any id's in phones, emails, etc.
1597    if (clonedContact.phoneNumbers) {
1598        for (i=0; i<clonedContact.phoneNumbers.length; i++) {
1599                clonedContact.phoneNumbers[i].id = null;
1600        }
1601    }
1602    if (clonedContact.emails) {
1603        for (i=0; i<clonedContact.emails.length; i++) {
1604                clonedContact.emails[i].id = null;
1605        }
1606    }
1607    if (clonedContact.addresses) {
1608        for (i=0; i<clonedContact.addresses.length; i++) {
1609                clonedContact.addresses[i].id = null;
1610        }
1611    }
1612    if (clonedContact.ims) {
1613        for (i=0; i<clonedContact.ims.length; i++) {
1614                clonedContact.ims[i].id = null;
1615        }
1616    }
1617    if (clonedContact.organizations) {
1618        for (i=0; i<clonedContact.organizations.length; i++) {
1619                clonedContact.organizations[i].id = null;
1620        }
1621    }
1622    if (clonedContact.photos) {
1623        for (i=0; i<clonedContact.photos.length; i++) {
1624                clonedContact.photos[i].id = null;
1625        }
1626    }
1627    if (clonedContact.urls) {
1628        for (i=0; i<clonedContact.urls.length; i++) {
1629                clonedContact.urls[i].id = null;
1630        }
1631    }
1632    return clonedContact;
1633};
1634
1635/**
1636* Persists contact to device storage.
1637* @param successCB success callback
1638* @param errorCB error callback - optional
1639*/
1640Contact.prototype.save = function(successCB, errorCB) {
1641        // don't modify the original contact
1642        var cloned = PhoneGap.clone(this);
1643        cloned.convertDatesOut(); 
1644        PhoneGap.exec(successCB, errorCB, "com.phonegap.contacts","save", [{"contact": cloned}]);
1645};
1646
1647/**
1648* Contact name.
1649* @param formatted
1650* @param familyName
1651* @param givenName
1652* @param middle
1653* @param prefix
1654* @param suffix
1655*/
1656var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
1657    this.formatted = formatted != "undefined" ? formatted : null;
1658    this.familyName = familyName != "undefined" ? familyName : null;
1659    this.givenName = givenName != "undefined" ? givenName : null;
1660    this.middleName = middle != "undefined" ? middle : null;
1661    this.honorificPrefix = prefix != "undefined" ? prefix : null;
1662    this.honorificSuffix = suffix != "undefined" ? suffix : null;
1663};
1664
1665/**
1666* Generic contact field.
1667* @param type
1668* @param value
1669* @param pref
1670* @param id
1671*/
1672var ContactField = function(type, value, pref, id) {
1673    this.type = type != "undefined" ? type : null;
1674    this.value = value != "undefined" ? value : null;
1675    this.pref = pref != "undefined" ? pref : null;
1676    this.id = id != "undefined" ? id : null;
1677};
1678
1679/**
1680* Contact address.
1681* @param pref - boolean is primary / preferred address
1682* @param type - string - work, home…..
1683* @param formatted
1684* @param streetAddress
1685* @param locality
1686* @param region
1687* @param postalCode
1688* @param country
1689*/
1690var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country, id) {
1691        this.pref = pref != "undefined" ? pref : null;
1692        this.type = type != "undefined" ? type : null;
1693    this.formatted = formatted != "undefined" ? formatted : null;
1694    this.streetAddress = streetAddress != "undefined" ? streetAddress : null;
1695    this.locality = locality != "undefined" ? locality : null;
1696    this.region = region != "undefined" ? region : null;
1697    this.postalCode = postalCode != "undefined" ? postalCode : null;
1698    this.country = country != "undefined" ? country : null;
1699    this.id = id != "undefined" ? id : null;
1700};
1701
1702/**
1703* Contact organization.
1704* @param pref - boolean is primary / preferred address
1705* @param type - string - work, home…..
1706* @param name
1707* @param dept
1708* @param title
1709*/
1710var ContactOrganization = function(pref, type, name, dept, title) {
1711        this.pref = pref != "undefined" ? pref : null;
1712        this.type = type != "undefined" ? type : null;
1713    this.name = name != "undefined" ? name : null;
1714    this.department = dept != "undefined" ? dept : null;
1715    this.title = title != "undefined" ? title : null;
1716};
1717
1718/**
1719* Contact account.
1720* @param domain
1721* @param username
1722* @param userid
1723*/
1724/*var ContactAccount = function(domain, username, userid) {
1725    this.domain = domain != "undefined" ? domain : null;
1726    this.username = username != "undefined" ? username : null;
1727    this.userid = userid != "undefined" ? userid : null;
1728}*/
1729
1730/**
1731* Represents a group of Contacts.
1732*/
1733var Contacts = function() {
1734    this.inProgress = false;
1735    this.records = new Array();
1736};
1737/**
1738* Returns an array of Contacts matching the search criteria.
1739* @param fields that should be searched
1740* @param successCB success callback
1741* @param errorCB error callback (optional)
1742* @param {ContactFindOptions} options that can be applied to contact searching
1743* @return array of Contacts matching search criteria
1744*/
1745Contacts.prototype.find = function(fields, successCB, errorCB, options) {
1746        if (successCB === null) {
1747        throw new TypeError("You must specify a success callback for the find command.");
1748    }
1749    if (fields === null || fields === "undefined" || fields.length === "undefined" || fields.length <= 0) {
1750        if (typeof errorCB === "function") {
1751                        errorCB({"code": ContactError.INVALID_ARGUMENT_ERROR});
1752        }
1753    } else {
1754                PhoneGap.exec(successCB, errorCB, "com.phonegap.contacts","search", [{"fields":fields, "findOptions":options}]);
1755    }
1756};
1757/**
1758* need to turn the array of JSON strings representing contact objects into actual objects
1759* @param array of JSON strings with contact data
1760* @return call results callback with array of Contact objects
1761*  This function is called from objective C Contacts.search() method.
1762*/
1763Contacts.prototype._findCallback = function(pluginResult) {
1764        var contacts = new Array();
1765        try {
1766                for (var i=0; i<pluginResult.message.length; i++) {
1767                        var newContact = navigator.contacts.create(pluginResult.message[i]); 
1768                        newContact.convertDatesIn();
1769                        contacts.push(newContact);
1770                }
1771                pluginResult.message = contacts;
1772        } catch(e){
1773                        console.log("Error parsing contacts: " +e);
1774        }
1775        return pluginResult;
1776}
1777
1778/**
1779* need to turn the JSON string representing contact object into actual object
1780* @param JSON string with contact data
1781* Call stored results function with  Contact object
1782*  This function is called from objective C Contacts remove and save methods
1783*/
1784Contacts.prototype._contactCallback = function(pluginResult)
1785{
1786        var newContact = null;
1787        if (pluginResult.message){
1788                try {
1789                        newContact = navigator.contacts.create(pluginResult.message);
1790                        newContact.convertDatesIn();
1791                } catch(e){
1792                        console.log("Error parsing contact");
1793                }
1794        }
1795        pluginResult.message = newContact;
1796        return pluginResult;
1797       
1798};
1799/**
1800* Need to return an error object rather than just a single error code
1801* @param error code
1802* Call optional error callback if found.
1803* Called from objective c find, remove, and save methods on error.
1804*/
1805Contacts.prototype._errCallback = function(pluginResult)
1806{
1807        var errorObj = new ContactError();
1808        errorObj.code = pluginResult.message;
1809        pluginResult.message = errorObj;
1810        return pluginResult;
1811};
1812// iPhone only api to create a new contact via the GUI
1813Contacts.prototype.newContactUI = function(successCallback) { 
1814    PhoneGap.exec(successCallback, null, "com.phonegap.contacts","newContact", []);
1815};
1816// iPhone only api to select a contact via the GUI
1817Contacts.prototype.chooseContact = function(successCallback, options) {
1818    PhoneGap.exec(successCallback, null, "com.phonegap.contacts","chooseContact", options);
1819};
1820
1821
1822/**
1823* This function creates a new contact, but it does not persist the contact
1824* to device storage. To persist the contact to device storage, invoke
1825* contact.save().
1826* @param properties an object who's properties will be examined to create a new Contact
1827* @returns new Contact object
1828*/
1829Contacts.prototype.create = function(properties) {
1830    var i;
1831    var contact = new Contact();
1832    for (i in properties) {
1833        if (contact[i] !== 'undefined') {
1834            contact[i] = properties[i];
1835        }
1836    }
1837    return contact;
1838};
1839
1840/**
1841 * ContactFindOptions.
1842 * @param filter used to match contacts against
1843 * @param multiple boolean used to determine if more than one contact should be returned
1844 */
1845var ContactFindOptions = function(filter, multiple, updatedSince) {
1846    this.filter = filter || '';
1847    this.multiple = multiple || false;
1848};
1849
1850/**
1851 *  ContactError.
1852 *  An error code assigned by an implementation when an error has occurred
1853 */
1854var ContactError = function() {
1855    this.code=null;
1856};
1857
1858/**
1859 * Error codes
1860 */
1861ContactError.UNKNOWN_ERROR = 0;
1862ContactError.INVALID_ARGUMENT_ERROR = 1;
1863ContactError.TIMEOUT_ERROR = 2;
1864ContactError.PENDING_OPERATION_ERROR = 3;
1865ContactError.IO_ERROR = 4;
1866ContactError.NOT_SUPPORTED_ERROR = 5;
1867ContactError.PERMISSION_DENIED_ERROR = 20;
1868
1869/**
1870 * Add the contact interface into the browser.
1871 */
1872PhoneGap.addConstructor(function() { 
1873    if(typeof navigator.contacts == "undefined") {
1874        navigator.contacts = new Contacts();
1875    }
1876});
1877};
1878if (!PhoneGap.hasResource("file")) {
1879        PhoneGap.addResource("file");
1880
1881/**
1882 * This class provides generic read and write access to the mobile device file system.
1883 * They are not used to read files from a server.
1884 */
1885
1886/**
1887 * This class provides some useful information about a file.
1888 * This is the fields returned when navigator.fileMgr.getFileProperties()
1889 * is called.
1890 */
1891FileProperties = function(filePath) {
1892    this.filePath = filePath;
1893    this.size = 0;
1894    this.lastModifiedDate = null;
1895}
1896/**
1897 * Represents a single file.
1898 *
1899 * name {DOMString} name of the file, without path information
1900 * fullPath {DOMString} the full path of the file, including the name
1901 * type {DOMString} mime type
1902 * lastModifiedDate {Date} last modified date
1903 * size {Number} size of the file in bytes
1904 */
1905File = function(name, fullPath, type, lastModifiedDate, size) {
1906        this.name = name || null;
1907    this.fullPath = fullPath || null;
1908        this.type = type || null;
1909    this.lastModifiedDate = lastModifiedDate || null;
1910    this.size = size || 0;
1911}
1912/**
1913 * Create an event object since we can't set target on DOM event.
1914 *
1915 * @param type
1916 * @param target
1917 *
1918 */
1919File._createEvent = function(type, target) {
1920    // Can't create event object, since we can't set target (its readonly)
1921    //var evt = document.createEvent('Events');
1922    //evt.initEvent("onload", false, false);
1923    var evt = {"type": type};
1924    evt.target = target;
1925    return evt;
1926};
1927
1928FileError = function() {
1929   this.code = null;
1930}
1931
1932// File error codes
1933// Found in DOMException
1934FileError.NOT_FOUND_ERR = 1;
1935FileError.SECURITY_ERR = 2;
1936FileError.ABORT_ERR = 3;
1937
1938// Added by this specification
1939FileError.NOT_READABLE_ERR = 4;
1940FileError.ENCODING_ERR = 5;
1941FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
1942FileError.INVALID_STATE_ERR = 7;
1943FileError.SYNTAX_ERR = 8;
1944FileError.INVALID_MODIFICATION_ERR = 9;
1945FileError.QUOTA_EXCEEDED_ERR = 10;
1946FileError.TYPE_MISMATCH_ERR = 11;
1947FileError.PATH_EXISTS_ERR = 12;
1948
1949//-----------------------------------------------------------------------------
1950// File manager
1951//-----------------------------------------------------------------------------
1952
1953FileMgr = function() {
1954}
1955
1956FileMgr.prototype.testFileExists = function(fileName, successCallback, errorCallback) {
1957    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "testFileExists", [fileName]);
1958};
1959
1960FileMgr.prototype.testDirectoryExists = function(dirName, successCallback, errorCallback) {
1961    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "testDirectoryExists", [dirName]);
1962};
1963
1964FileMgr.prototype.getFreeDiskSpace = function(successCallback, errorCallback) {
1965    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getFreeDiskSpace", []);
1966};
1967
1968FileMgr.prototype.write = function(fileName, data, position, successCallback, errorCallback) {
1969    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "write", [fileName, data, position]);
1970};
1971
1972FileMgr.prototype.truncate = function(fileName, size, successCallback, errorCallback) {
1973    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "truncateFile", [fileName, size]);
1974};
1975
1976FileMgr.prototype.readAsText = function(fileName, encoding, successCallback, errorCallback) {
1977    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "readFile", [fileName, encoding]);
1978};
1979
1980FileMgr.prototype.readAsDataURL = function(fileName, successCallback, errorCallback) {
1981        PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "readAsDataURL", [fileName]);
1982};
1983
1984PhoneGap.addConstructor(function() {
1985    if (typeof navigator.fileMgr === "undefined") {
1986        navigator.fileMgr = new FileMgr();
1987    }
1988});
1989
1990
1991//-----------------------------------------------------------------------------
1992// File Reader
1993//-----------------------------------------------------------------------------
1994
1995/**
1996 * This class reads the mobile device file system.
1997 *
1998 */
1999FileReader = function() {
2000    this.fileName = "";
2001
2002    this.readyState = 0;
2003
2004    // File data
2005    this.result = null;
2006
2007    // Error
2008    this.error = null;
2009
2010    // Event handlers
2011    this.onloadstart = null;    // When the read starts.
2012    this.onprogress = null;     // While reading (and decoding) file or fileBlob data, and reporting partial file data (progess.loaded/progress.total)
2013    this.onload = null;         // When the read has successfully completed.
2014    this.onerror = null;        // When the read has failed (see errors).
2015    this.onloadend = null;      // When the request has completed (either in success or failure).
2016    this.onabort = null;        // When the read has been aborted. For instance, by invoking the abort() method.
2017}
2018
2019// States
2020FileReader.EMPTY = 0;
2021FileReader.LOADING = 1;
2022FileReader.DONE = 2;
2023
2024/**
2025 * Abort reading file.
2026 */
2027FileReader.prototype.abort = function() {
2028    var evt;
2029    this.readyState = FileReader.DONE;
2030    this.result = null;
2031
2032    // set error
2033    var error = new FileError();
2034    error.code = error.ABORT_ERR;
2035    this.error = error;
2036   
2037    // If error callback
2038    if (typeof this.onerror === "function") {
2039        evt = File._createEvent("error", this);
2040        this.onerror(evt);
2041    }
2042    // If abort callback
2043    if (typeof this.onabort === "function") {
2044        evt = File._createEvent("abort", this);
2045        this.onabort(evt);
2046    }
2047    // If load end callback
2048    if (typeof this.onloadend === "function") {
2049        evt = File._createEvent("loadend", this);
2050        this.onloadend(evt);
2051    }
2052};
2053
2054/**
2055 * Read text file.
2056 *
2057 * @param file          The name of the file
2058 * @param encoding      [Optional] (see http://www.iana.org/assignments/character-sets)
2059 */
2060FileReader.prototype.readAsText = function(file, encoding) {
2061    this.fileName = "";
2062        if (typeof file.fullPath === "undefined") {
2063                this.fileName = file;
2064        } else {
2065                this.fileName = file.fullPath;
2066        }
2067
2068    // LOADING state
2069    this.readyState = FileReader.LOADING;
2070
2071    // If loadstart callback
2072    if (typeof this.onloadstart === "function") {
2073        var evt = File._createEvent("loadstart", this);
2074        this.onloadstart(evt);
2075    }
2076
2077    // Default encoding is UTF-8
2078    var enc = encoding ? encoding : "UTF-8";
2079
2080    var me = this;
2081
2082    // Read file
2083    navigator.fileMgr.readAsText(this.fileName, enc,
2084
2085        // Success callback
2086        function(r) {
2087            var evt;
2088
2089            // If DONE (cancelled), then don't do anything
2090            if (me.readyState === FileReader.DONE) {
2091                return;
2092            }
2093
2094            // Save result
2095            me.result = decodeURIComponent(r);
2096
2097            // If onload callback
2098            if (typeof me.onload === "function") {
2099                evt = File._createEvent("load", me);
2100                me.onload(evt);
2101            }
2102
2103            // DONE state
2104            me.readyState = FileReader.DONE;
2105
2106            // If onloadend callback
2107            if (typeof me.onloadend === "function") {
2108                evt = File._createEvent("loadend", me);
2109                me.onloadend(evt);
2110            }
2111        },
2112
2113        // Error callback
2114        function(e) {
2115            var evt;
2116            // If DONE (cancelled), then don't do anything
2117            if (me.readyState === FileReader.DONE) {
2118                return;
2119            }
2120
2121            // Save error
2122            me.error = e;
2123
2124            // If onerror callback
2125            if (typeof me.onerror === "function") {
2126                evt = File._createEvent("error", me);
2127                me.onerror(evt);
2128            }
2129
2130            // DONE state
2131            me.readyState = FileReader.DONE;
2132
2133            // If onloadend callback
2134            if (typeof me.onloadend === "function") {
2135                evt = File._createEvent("loadend", me);
2136                me.onloadend(evt);
2137            }
2138        }
2139        );
2140};
2141
2142
2143/**
2144 * Read file and return data as a base64 encoded data url.
2145 * A data url is of the form:
2146 *      data:[<mediatype>][;base64],<data>
2147 *
2148 * @param file          {File} File object containing file properties
2149 */
2150FileReader.prototype.readAsDataURL = function(file) {
2151    this.fileName = "";
2152   
2153    if (typeof file.fullPath === "undefined") {
2154        this.fileName = file;
2155    } else {
2156        this.fileName = file.fullPath;
2157    }
2158
2159    // LOADING state
2160    this.readyState = FileReader.LOADING;
2161
2162    // If loadstart callback
2163    if (typeof this.onloadstart === "function") {
2164        var evt = File._createEvent("loadstart", this);
2165        this.onloadstart(evt);
2166    }
2167
2168    var me = this;
2169
2170    // Read file
2171    navigator.fileMgr.readAsDataURL(this.fileName,
2172
2173        // Success callback
2174        function(r) {
2175            var evt;
2176
2177            // If DONE (cancelled), then don't do anything
2178            if (me.readyState === FileReader.DONE) {
2179                return;
2180            }
2181
2182            // Save result
2183            me.result = r;
2184
2185            // If onload callback
2186            if (typeof me.onload === "function") {
2187                evt = File._createEvent("load", me);
2188                me.onload(evt);
2189            }
2190
2191            // DONE state
2192            me.readyState = FileReader.DONE;
2193
2194            // If onloadend callback
2195            if (typeof me.onloadend === "function") {
2196                evt = File._createEvent("loadend", me);
2197                me.onloadend(evt);
2198            }
2199        },
2200
2201        // Error callback
2202        function(e) {
2203            var evt;
2204            // If DONE (cancelled), then don't do anything
2205            if (me.readyState === FileReader.DONE) {
2206                return;
2207            }
2208
2209            // Save error
2210            me.error = e;
2211
2212            // If onerror callback
2213            if (typeof me.onerror === "function") {
2214                evt = File._createEvent("error", me);
2215                me.onerror(evt);
2216            }
2217
2218            // DONE state
2219            me.readyState = FileReader.DONE;
2220
2221            // If onloadend callback
2222            if (typeof me.onloadend === "function") {
2223                evt = File._createEvent("loadend", me);
2224                me.onloadend(evt);
2225            }
2226        }
2227        );
2228};
2229
2230/**
2231 * Read file and return data as a binary data.
2232 *
2233 * @param file          The name of the file
2234 */
2235FileReader.prototype.readAsBinaryString = function(file) {
2236    // TODO - Can't return binary data to browser.
2237    this.fileName = file;
2238};
2239
2240/**
2241 * Read file and return data as a binary data.
2242 *
2243 * @param file          The name of the file
2244 */
2245FileReader.prototype.readAsArrayBuffer = function(file) {
2246    // TODO - Can't return binary data to browser.
2247    this.fileName = file;
2248};
2249
2250//-----------------------------------------------------------------------------
2251// File Writer
2252//-----------------------------------------------------------------------------
2253
2254/**
2255 * This class writes to the mobile device file system.
2256 *
2257  @param file {File} a File object representing a file on the file system
2258*/
2259FileWriter = function(file) {
2260    this.fileName = "";
2261    this.length = 0;
2262        if (file) {
2263            this.fileName = file.fullPath || file;
2264            this.length = file.size || 0;
2265        }
2266       
2267        // default is to write at the beginning of the file
2268    this.position = 0;
2269   
2270    this.readyState = 0; // EMPTY
2271
2272    this.result = null;
2273
2274    // Error
2275    this.error = null;
2276
2277    // Event handlers
2278    this.onwritestart = null;   // When writing starts
2279    this.onprogress = null;             // While writing the file, and reporting partial file data
2280    this.onwrite = null;                // When the write has successfully completed.
2281    this.onwriteend = null;             // When the request has completed (either in success or failure).
2282    this.onabort = null;                // When the write has been aborted. For instance, by invoking the abort() method.
2283    this.onerror = null;                // When the write has failed (see errors).
2284}
2285
2286// States
2287FileWriter.INIT = 0;
2288FileWriter.WRITING = 1;
2289FileWriter.DONE = 2;
2290
2291/**
2292 * Abort writing file.
2293 */
2294FileWriter.prototype.abort = function() {
2295    // check for invalid state
2296        if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) {
2297                throw FileError.INVALID_STATE_ERR;
2298        } 
2299
2300    // set error
2301    var error = new FileError(), evt;
2302    error.code = error.ABORT_ERR;
2303    this.error = error;
2304   
2305    // If error callback
2306    if (typeof this.onerror === "function") {
2307        evt = File._createEvent("error", this);
2308        this.onerror(evt);
2309    }
2310    // If abort callback
2311    if (typeof this.onabort === "function") {
2312        evt = File._createEvent("abort", this);
2313        this.onabort(evt);
2314    }
2315   
2316    this.readyState = FileWriter.DONE;
2317
2318    // If write end callback
2319    if (typeof this.onwriteend == "function") {
2320        evt = File._createEvent("writeend", this);
2321        this.onwriteend(evt);
2322    }
2323};
2324
2325/**
2326 * @Deprecated: use write instead
2327 *
2328 * @param file to write the data to
2329 * @param text to be written
2330 * @param bAppend if true write to end of file, otherwise overwrite the file
2331 */
2332FileWriter.prototype.writeAsText = function(file, text, bAppend) {
2333        // Throw an exception if we are already writing a file
2334        if (this.readyState === FileWriter.WRITING) {
2335                throw FileError.INVALID_STATE_ERR;
2336        }
2337
2338        if (bAppend !== true) {
2339        bAppend = false; // for null values
2340    }
2341
2342    this.fileName = file;
2343
2344    // WRITING state
2345    this.readyState = FileWriter.WRITING;
2346
2347    var me = this;
2348
2349    // If onwritestart callback
2350    if (typeof me.onwritestart === "function") {
2351        var evt = File._createEvent("writestart", me);
2352        me.onwritestart(evt);
2353    }
2354       
2355       
2356    // Write file
2357        navigator.fileMgr.writeAsText(file, text, bAppend,
2358        // Success callback
2359        function(r) {
2360            var evt;
2361
2362            // If DONE (cancelled), then don't do anything
2363            if (me.readyState === FileWriter.DONE) {
2364                return;
2365            }
2366
2367            // Save result
2368            me.result = r;
2369
2370            // If onwrite callback
2371            if (typeof me.onwrite === "function") {
2372                evt = File._createEvent("write", me);
2373                me.onwrite(evt);
2374            }
2375
2376            // DONE state
2377            me.readyState = FileWriter.DONE;
2378
2379            // If onwriteend callback
2380            if (typeof me.onwriteend === "function") {
2381                evt = File._createEvent("writeend", me);
2382                me.onwriteend(evt);
2383            }
2384        },
2385
2386        // Error callback
2387        function(e) {
2388            var evt;
2389
2390            // If DONE (cancelled), then don't do anything
2391            if (me.readyState === FileWriter.DONE) {
2392                return;
2393            }
2394
2395            // Save error
2396            me.error = e;
2397
2398            // If onerror callback
2399            if (typeof me.onerror === "function") {
2400                evt = File._createEvent("error", me);
2401                me.onerror(evt);
2402            }
2403
2404            // DONE state
2405            me.readyState = FileWriter.DONE;
2406
2407            // If onwriteend callback
2408            if (typeof me.onwriteend === "function") {
2409                evt = File._createEvent("writeend", me);
2410                me.onwriteend(evt);
2411            }
2412        }
2413    );
2414};
2415
2416/**
2417 * Writes data to the file
2418 * 
2419 * @param text to be written
2420 */
2421FileWriter.prototype.write = function(text) {
2422        // Throw an exception if we are already writing a file
2423        if (this.readyState === FileWriter.WRITING) {
2424                throw FileError.INVALID_STATE_ERR;
2425        }
2426
2427    // WRITING state
2428    this.readyState = FileWriter.WRITING;
2429
2430    var me = this;
2431
2432    // If onwritestart callback
2433    if (typeof me.onwritestart === "function") {
2434        var evt = File._createEvent("writestart", me);
2435        me.onwritestart(evt);
2436    }
2437
2438    // Write file
2439    navigator.fileMgr.write(this.fileName, text, this.position,
2440
2441        // Success callback
2442        function(r) {
2443            var evt;
2444            // If DONE (cancelled), then don't do anything
2445            if (me.readyState === FileWriter.DONE) {
2446                return;
2447            }
2448
2449           
2450            // position always increases by bytes written because file would be extended
2451            me.position += r;
2452                        // The length of the file is now where we are done writing.
2453                        me.length = me.position;
2454           
2455            // If onwrite callback
2456            if (typeof me.onwrite === "function") {
2457                evt = File._createEvent("write", me);
2458                me.onwrite(evt);
2459            }
2460
2461            // DONE state
2462            me.readyState = FileWriter.DONE;
2463
2464            // If onwriteend callback
2465            if (typeof me.onwriteend === "function") {
2466                evt = File._createEvent("writeend", me);
2467                me.onwriteend(evt);
2468            }
2469        },
2470
2471        // Error callback
2472        function(e) {
2473            var evt;
2474
2475            // If DONE (cancelled), then don't do anything
2476            if (me.readyState === FileWriter.DONE) {
2477                return;
2478            }
2479
2480            // Save error
2481            me.error = e;
2482
2483            // If onerror callback
2484            if (typeof me.onerror === "function") {
2485                evt = File._createEvent("error", me);
2486                me.onerror(evt);
2487            }
2488
2489            // DONE state
2490            me.readyState = FileWriter.DONE;
2491
2492            // If onwriteend callback
2493            if (typeof me.onwriteend === "function") {
2494                evt = File._createEvent("writeend", me);
2495                me.onwriteend(evt);
2496            }
2497        }
2498        );
2499
2500};
2501
2502/**
2503 * Moves the file pointer to the location specified.
2504 *
2505 * If the offset is a negative number the position of the file
2506 * pointer is rewound.  If the offset is greater than the file
2507 * size the position is set to the end of the file. 
2508 *
2509 * @param offset is the location to move the file pointer to.
2510 */
2511FileWriter.prototype.seek = function(offset) {
2512    // Throw an exception if we are already writing a file
2513    if (this.readyState === FileWriter.WRITING) {
2514        throw FileError.INVALID_STATE_ERR;
2515    }
2516
2517    if (!offset) {
2518        return;
2519    }
2520   
2521    // See back from end of file.
2522    if (offset < 0) {
2523                this.position = Math.max(offset + this.length, 0);
2524        }
2525    // Offset is bigger then file size so set position
2526    // to the end of the file.
2527        else if (offset > this.length) {
2528                this.position = this.length;
2529        }
2530    // Offset is between 0 and file size so set the position
2531    // to start writing.
2532        else {
2533                this.position = offset;
2534        }       
2535};
2536
2537/**
2538 * Truncates the file to the size specified.
2539 *
2540 * @param size to chop the file at.
2541 */
2542FileWriter.prototype.truncate = function(size) {
2543        // Throw an exception if we are already writing a file
2544        if (this.readyState === FileWriter.WRITING) {
2545                throw FileError.INVALID_STATE_ERR;
2546        }
2547        // what if no size specified?
2548
2549    // WRITING state
2550    this.readyState = FileWriter.WRITING;
2551
2552    var me = this;
2553
2554    // If onwritestart callback
2555    if (typeof me.onwritestart === "function") {
2556        var evt = File._createEvent("writestart", me);
2557        me.onwritestart(evt);
2558    }
2559
2560    // Write file
2561    navigator.fileMgr.truncate(this.fileName, size,
2562
2563        // Success callback
2564        function(r) {
2565            var evt;
2566            // If DONE (cancelled), then don't do anything
2567            if (me.readyState === FileWriter.DONE) {
2568                return;
2569            }
2570
2571            // Update the length of the file
2572            me.length = r;
2573            me.position = Math.min(me.position, r);
2574
2575            // If onwrite callback
2576            if (typeof me.onwrite === "function") {
2577                evt = File._createEvent("write", me);
2578                me.onwrite(evt);
2579            }
2580
2581            // DONE state
2582            me.readyState = FileWriter.DONE;
2583
2584            // If onwriteend callback
2585            if (typeof me.onwriteend === "function") {
2586                evt = File._createEvent("writeend", me);
2587                me.onwriteend(evt);
2588            }
2589        },
2590
2591        // Error callback
2592        function(e) {
2593            var evt;
2594            // If DONE (cancelled), then don't do anything
2595            if (me.readyState === FileWriter.DONE) {
2596                return;
2597            }
2598
2599            // Save error
2600            me.error = e;
2601
2602            // If onerror callback
2603            if (typeof me.onerror === "function") {
2604                evt = File._createEvent("error", me);
2605                me.onerror(evt);
2606            }
2607
2608            // DONE state
2609            me.readyState = FileWriter.DONE;
2610
2611            // If onwriteend callback
2612            if (typeof me.onwriteend === "function") {
2613                evt = File._createEvent("writeend", me);
2614                me.onwriteend(evt);
2615            }
2616        }
2617    );
2618};
2619
2620LocalFileSystem = function() {
2621};
2622
2623// File error codes
2624LocalFileSystem.TEMPORARY = 0;
2625LocalFileSystem.PERSISTENT = 1;
2626LocalFileSystem.RESOURCE = 2;
2627LocalFileSystem.APPLICATION = 3;
2628
2629/**
2630 * Requests a filesystem in which to store application data.
2631 *
2632 * @param {int} type of file system being requested
2633 * @param {Function} successCallback is called with the new FileSystem
2634 * @param {Function} errorCallback is called with a FileError
2635 */
2636LocalFileSystem.prototype.requestFileSystem = function(type, size, successCallback, errorCallback) {
2637        if (type < 0 || type > 3) {
2638                if (typeof errorCallback == "function") {
2639                        errorCallback({
2640                                "code": FileError.SYNTAX_ERR
2641                        });
2642                }
2643        }
2644        else {
2645                PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "requestFileSystem", [type, size]);
2646        }
2647};
2648
2649/**
2650 *
2651 * @param {DOMString} uri referring to a local file in a filesystem
2652 * @param {Function} successCallback is called with the new entry
2653 * @param {Function} errorCallback is called with a FileError
2654 */
2655LocalFileSystem.prototype.resolveLocalFileSystemURI = function(uri, successCallback, errorCallback) {
2656    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "resolveLocalFileSystemURI", [uri]);
2657};
2658
2659/**
2660* This function  is required as we need to convert raw
2661* JSON objects into concrete File and Directory objects. 
2662*
2663* @param a JSON Objects that need to be converted to DirectoryEntry or FileEntry objects.
2664* @returns an entry
2665*/
2666LocalFileSystem.prototype._castFS = function(pluginResult) {
2667    var entry = null;
2668    entry = new DirectoryEntry();
2669    entry.isDirectory = pluginResult.message.root.isDirectory;
2670    entry.isFile = pluginResult.message.root.isFile;
2671    entry.name = pluginResult.message.root.name;
2672    entry.fullPath = pluginResult.message.root.fullPath;
2673    pluginResult.message.root = entry;
2674    return pluginResult;   
2675}
2676
2677LocalFileSystem.prototype._castEntry = function(pluginResult) {
2678    var entry = null;
2679    if (pluginResult.message.isDirectory) {
2680        entry = new DirectoryEntry();
2681    }
2682    else if (pluginResult.message.isFile) {
2683                entry = new FileEntry();
2684    }
2685    entry.isDirectory = pluginResult.message.isDirectory;
2686    entry.isFile = pluginResult.message.isFile;
2687    entry.name = pluginResult.message.name;
2688    entry.fullPath = pluginResult.message.fullPath;
2689    pluginResult.message = entry;
2690    return pluginResult;   
2691}
2692
2693LocalFileSystem.prototype._castEntries = function(pluginResult) {
2694    var entries = pluginResult.message;
2695        var retVal = []; 
2696        for (i=0; i<entries.length; i++) {
2697                retVal.push(window.localFileSystem._createEntry(entries[i]));
2698        }
2699    pluginResult.message = retVal;
2700    return pluginResult;   
2701}
2702
2703LocalFileSystem.prototype._createEntry = function(castMe) {
2704        var entry = null;
2705    if (castMe.isDirectory) {
2706        entry = new DirectoryEntry();
2707    }
2708    else if (castMe.isFile) {
2709        entry = new FileEntry();
2710    }
2711    entry.isDirectory = castMe.isDirectory;
2712    entry.isFile = castMe.isFile;
2713    entry.name = castMe.name;
2714    entry.fullPath = castMe.fullPath;
2715    return entry;   
2716
2717}
2718
2719LocalFileSystem.prototype._castDate = function(pluginResult) {
2720        if (pluginResult.message.modificationTime) {
2721                var metadataObj = new Metadata();
2722               
2723            metadataObj.modificationTime = new Date(pluginResult.message.modificationTime);
2724            pluginResult.message = metadataObj;
2725        }
2726        else if (pluginResult.message.lastModifiedDate) {
2727                var file = new File();
2728        file.size = pluginResult.message.size;
2729        file.type = pluginResult.message.type;
2730        file.name = pluginResult.message.name;
2731        file.fullPath = pluginResult.message.fullPath;
2732                file.lastModifiedDate = new Date(pluginResult.message.lastModifiedDate);
2733            pluginResult.message = file;               
2734        }
2735
2736    return pluginResult;       
2737}
2738LocalFileSystem.prototype._castError = function(pluginResult) {
2739        var fileError = new FileError();
2740        fileError.code = pluginResult.message;
2741        pluginResult.message = fileError;
2742        return pluginResult;
2743}
2744
2745/**
2746 * Information about the state of the file or directory
2747 *
2748 * {Date} modificationTime (readonly)
2749 */
2750Metadata = function() {
2751    this.modificationTime=null;
2752};
2753
2754/**
2755 * Supplies arguments to methods that lookup or create files and directories
2756 *
2757 * @param {boolean} create file or directory if it doesn't exist
2758 * @param {boolean} exclusive if true the command will fail if the file or directory exists
2759 */
2760Flags = function(create, exclusive) {
2761    this.create = create || false;
2762    this.exclusive = exclusive || false;
2763};
2764
2765/**
2766 * An interface representing a file system
2767 *
2768 * {DOMString} name the unique name of the file system (readonly)
2769 * {DirectoryEntry} root directory of the file system (readonly)
2770 */
2771FileSystem = function() {
2772    this.name = null;
2773    this.root = null;
2774};
2775
2776/**
2777 * An interface representing a directory on the file system.
2778 *
2779 * {boolean} isFile always false (readonly)
2780 * {boolean} isDirectory always true (readonly)
2781 * {DOMString} name of the directory, excluding the path leading to it (readonly)
2782 * {DOMString} fullPath the absolute full path to the directory (readonly)
2783 * {FileSystem} filesystem on which the directory resides (readonly)
2784 */
2785DirectoryEntry = function() {
2786    this.isFile = false;
2787    this.isDirectory = true;
2788    this.name = null;
2789    this.fullPath = null;
2790    this.filesystem = null;
2791};
2792
2793/**
2794 * Copies a directory to a new location
2795 *
2796 * @param {DirectoryEntry} parent the directory to which to copy the entry
2797 * @param {DOMString} newName the new name of the entry, defaults to the current name
2798 * @param {Function} successCallback is called with the new entry
2799 * @param {Function} errorCallback is called with a FileError
2800 */
2801DirectoryEntry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
2802    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "copyTo", [this.fullPath, parent, newName]);
2803};
2804
2805/**
2806 * Looks up the metadata of the entry
2807 *
2808 * @param {Function} successCallback is called with a Metadata object
2809 * @param {Function} errorCallback is called with a FileError
2810 */
2811DirectoryEntry.prototype.getMetadata = function(successCallback, errorCallback) {
2812    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getMetadata", [this.fullPath]);
2813};
2814
2815/**
2816 * Gets the parent of the entry
2817 *
2818 * @param {Function} successCallback is called with a parent entry
2819 * @param {Function} errorCallback is called with a FileError
2820 */
2821DirectoryEntry.prototype.getParent = function(successCallback, errorCallback) {
2822    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getParent", [this.fullPath]);
2823};
2824
2825/**
2826 * Moves a directory to a new location
2827 *
2828 * @param {DirectoryEntry} parent the directory to which to move the entry
2829 * @param {DOMString} newName the new name of the entry, defaults to the current name
2830 * @param {Function} successCallback is called with the new entry
2831 * @param {Function} errorCallback is called with a FileError
2832 */
2833DirectoryEntry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
2834    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "moveTo", [this.fullPath, parent, newName]);
2835};
2836
2837/**
2838 * Removes the entry
2839 *
2840 * @param {Function} successCallback is called with no parameters
2841 * @param {Function} errorCallback is called with a FileError
2842 */
2843DirectoryEntry.prototype.remove = function(successCallback, errorCallback) {
2844    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "remove", [this.fullPath]);
2845};
2846
2847/**
2848 * Returns a URI that can be used to identify this entry.
2849 *
2850 * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
2851 * @param {Function} successCallback is called with the new entry
2852 * @param {Function} errorCallback is called with a FileError
2853 */
2854DirectoryEntry.prototype.toURI = function(mimeType, successCallback, errorCallback) {
2855    return "file://localhost" + this.fullPath;
2856    //PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "toURI", [this.fullPath, mimeType]);
2857};
2858
2859/**
2860 * Creates a new DirectoryReader to read entries from this directory
2861 */
2862DirectoryEntry.prototype.createReader = function(successCallback, errorCallback) {
2863    return new DirectoryReader(this.fullPath);
2864};
2865
2866/**
2867 * Creates or looks up a directory
2868 *
2869 * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
2870 * @param {Flags} options to create or excluively create the directory
2871 * @param {Function} successCallback is called with the new entry
2872 * @param {Function} errorCallback is called with a FileError
2873 */
2874DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {
2875    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getDirectory", [this.fullPath, path, options]);
2876};
2877
2878/**
2879 * Creates or looks up a file
2880 *
2881 * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
2882 * @param {Flags} options to create or excluively create the file
2883 * @param {Function} successCallback is called with the new entry
2884 * @param {Function} errorCallback is called with a FileError
2885 */
2886DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
2887    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getFile", [this.fullPath, path, options]);
2888};
2889
2890/**
2891 * Deletes a directory and all of it's contents
2892 *
2893 * @param {Function} successCallback is called with no parameters
2894 * @param {Function} errorCallback is called with a FileError
2895 */
2896DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {
2897    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "removeRecursively", [this.fullPath]);
2898};
2899
2900/**
2901 * An interface that lists the files and directories in a directory.
2902 */
2903DirectoryReader = function(fullPath){
2904        this.fullPath = fullPath || null;   
2905};
2906
2907/**
2908 * Returns a list of entries from a directory.
2909 *
2910 * @param {Function} successCallback is called with a list of entries
2911 * @param {Function} errorCallback is called with a FileError
2912 */
2913DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
2914    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "readEntries", [this.fullPath]);
2915}
2916 
2917/**
2918 * An interface representing a directory on the file system.
2919 *
2920 * {boolean} isFile always true (readonly)
2921 * {boolean} isDirectory always false (readonly)
2922 * {DOMString} name of the file, excluding the path leading to it (readonly)
2923 * {DOMString} fullPath the absolute full path to the file (readonly)
2924 * {FileSystem} filesystem on which the directory resides (readonly)
2925 */
2926FileEntry = function() {
2927    this.isFile = true;
2928    this.isDirectory = false;
2929    this.name = null;
2930    this.fullPath = null;
2931    this.filesystem = null;
2932};
2933
2934/**
2935 * Copies a file to a new location
2936 *
2937 * @param {DirectoryEntry} parent the directory to which to copy the entry
2938 * @param {DOMString} newName the new name of the entry, defaults to the current name
2939 * @param {Function} successCallback is called with the new entry
2940 * @param {Function} errorCallback is called with a FileError
2941 */
2942FileEntry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
2943    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "copyTo", [this.fullPath, parent, newName]);
2944};
2945
2946/**
2947 * Looks up the metadata of the entry
2948 *
2949 * @param {Function} successCallback is called with a Metadata object
2950 * @param {Function} errorCallback is called with a FileError
2951 */
2952FileEntry.prototype.getMetadata = function(successCallback, errorCallback) {
2953    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getMetadata", [this.fullPath]);
2954};
2955
2956/**
2957 * Gets the parent of the entry
2958 *
2959 * @param {Function} successCallback is called with a parent entry
2960 * @param {Function} errorCallback is called with a FileError
2961 */
2962FileEntry.prototype.getParent = function(successCallback, errorCallback) {
2963    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getParent", [this.fullPath]);
2964};
2965
2966/**
2967 * Moves a directory to a new location
2968 *
2969 * @param {DirectoryEntry} parent the directory to which to move the entry
2970 * @param {DOMString} newName the new name of the entry, defaults to the current name
2971 * @param {Function} successCallback is called with the new entry
2972 * @param {Function} errorCallback is called with a FileError
2973 */
2974FileEntry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
2975    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "moveTo", [this.fullPath, parent, newName]);
2976};
2977
2978/**
2979 * Removes the entry
2980 *
2981 * @param {Function} successCallback is called with no parameters
2982 * @param {Function} errorCallback is called with a FileError
2983 */
2984FileEntry.prototype.remove = function(successCallback, errorCallback) {
2985    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "remove", [this.fullPath]);
2986};
2987
2988/**
2989 * Returns a URI that can be used to identify this entry.
2990 *
2991 * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
2992 * @param {Function} successCallback is called with the new entry
2993 * @param {Function} errorCallback is called with a FileError
2994 */
2995FileEntry.prototype.toURI = function(mimeType, successCallback, errorCallback) {
2996    return "file://localhost" + this.fullPath;
2997    //PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "toURI", [this.fullPath, mimeType]);
2998};
2999
3000/**
3001 * Creates a new FileWriter associated with the file that this FileEntry represents.
3002 *
3003 * @param {Function} successCallback is called with the new FileWriter
3004 * @param {Function} errorCallback is called with a FileError
3005 */
3006FileEntry.prototype.createWriter = function(successCallback, errorCallback) {
3007        this.file(function(filePointer) {       
3008                var writer = new FileWriter(filePointer);
3009                if (writer.fileName == null || writer.fileName == "") {
3010                        if (typeof errorCallback == "function") {
3011                                errorCallback({
3012                                        "code": FileError.INVALID_STATE_ERR
3013                                });
3014                }
3015                }
3016                if (typeof successCallback == "function") {
3017                        successCallback(writer);
3018                }       
3019        }, errorCallback);
3020};
3021
3022/**
3023 * Returns a File that represents the current state of the file that this FileEntry represents.
3024 *
3025 * @param {Function} successCallback is called with the new File object
3026 * @param {Function} errorCallback is called with a FileError
3027 */
3028FileEntry.prototype.file = function(successCallback, errorCallback) {
3029    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.file", "getFileMetadata", [this.fullPath]);
3030};
3031
3032/**
3033 * Add the FileSystem interface into the browser.
3034 */
3035PhoneGap.addConstructor(function() {
3036        var pgLocalFileSystem = new LocalFileSystem();
3037        // Needed for cast methods
3038    if(typeof window.localFileSystem == "undefined") window.localFileSystem  = pgLocalFileSystem;
3039    if(typeof window.requestFileSystem == "undefined") window.requestFileSystem  = pgLocalFileSystem.requestFileSystem;
3040    if(typeof window.resolveLocalFileSystemURI == "undefined") window.resolveLocalFileSystemURI = pgLocalFileSystem.resolveLocalFileSystemURI;
3041});
3042};
3043
3044
3045
3046
3047/*
3048 * PhoneGap is available under *either* the terms of the modified BSD license *or* the
3049 * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
3050 * 
3051 * Copyright (c) 2005-2011, Nitobi Software Inc.
3052 * Copyright (c) 2011, Matt Kane
3053 */
3054
3055if (!PhoneGap.hasResource("filetransfer")) {
3056        PhoneGap.addResource("filetransfer");
3057
3058/**
3059 * FileTransfer uploads a file to a remote server.
3060 */
3061FileTransfer = function() {}
3062
3063/**
3064 * FileUploadResult
3065 */
3066FileUploadResult = function() {
3067    this.bytesSent = 0;
3068    this.responseCode = null;
3069    this.response = null;
3070}
3071
3072/**
3073 * FileTransferError
3074 */
3075FileTransferError = function(errorCode) {
3076    this.code = errorCode || null;
3077}
3078
3079FileTransferError.FILE_NOT_FOUND_ERR = 1;
3080FileTransferError.INVALID_URL_ERR = 2;
3081FileTransferError.CONNECTION_ERR = 3;
3082
3083/**
3084* Given an absolute file path, uploads a file on the device to a remote server
3085* using a multipart HTTP request.
3086* @param filePath {String}           Full path of the file on the device
3087* @param server {String}             URL of the server to receive the file
3088* @param successCallback (Function}  Callback to be invoked when upload has completed
3089* @param errorCallback {Function}    Callback to be invoked upon error
3090* @param options {FileUploadOptions} Optional parameters such as file name and mimetype           
3091*/
3092FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options) {
3093        if(!options.params) {
3094                options.params = {};
3095        }
3096        options.filePath = filePath;
3097        options.server = server;
3098        if(!options.fileKey) {
3099                options.fileKey = 'file';
3100        }
3101        if(!options.fileName) {
3102                options.fileName = 'image.jpg';
3103        }
3104        if(!options.mimeType) {
3105                options.mimeType = 'image/jpeg';
3106        }
3107       
3108        // successCallback required
3109        if (typeof successCallback != "function") {
3110        console.log("FileTransfer Error: successCallback is not a function");
3111        return;
3112    }
3113
3114
3115    // errorCallback optional
3116    if (errorCallback && (typeof errorCallback != "function")) {
3117        console.log("FileTransfer Error: errorCallback is not a function");
3118        return;
3119    }
3120       
3121    PhoneGap.exec(successCallback, errorCallback, 'com.phonegap.filetransfer', 'upload', [options]);
3122};
3123
3124FileTransfer.prototype._castTransferError = function(pluginResult) {
3125        var fileError = new FileTransferError(pluginResult.message);
3126        //fileError.code = pluginResult.message;
3127        pluginResult.message = fileError;
3128        return pluginResult;
3129}
3130
3131FileTransfer.prototype._castUploadResult = function(pluginResult) {
3132        var result = new FileUploadResult();
3133        result.bytesSent = pluginResult.message.bytesSent;
3134        result.responseCode = pluginResult.message.responseCode;
3135        result.response = decodeURIComponent(pluginResult.message.response);
3136        pluginResult.message = result;
3137        return pluginResult;
3138}
3139
3140/**
3141 * Options to customize the HTTP request used to upload files.
3142 * @param fileKey {String}   Name of file request parameter.
3143 * @param fileName {String}  Filename to be used by the server. Defaults to image.jpg.
3144 * @param mimeType {String}  Mimetype of the uploaded file. Defaults to image/jpeg.
3145 * @param params {Object}    Object with key: value params to send to the server.
3146 */
3147FileUploadOptions = function(fileKey, fileName, mimeType, params) {
3148    this.fileKey = fileKey || null;
3149    this.fileName = fileName || null;
3150    this.mimeType = mimeType || null;
3151    this.params = params || null;
3152}
3153
3154
3155PhoneGap.addConstructor(function() {
3156    if (typeof navigator.fileTransfer == "undefined") navigator.fileTransfer = new FileTransfer();
3157});
3158};
3159if (!PhoneGap.hasResource("geolocation")) {
3160        PhoneGap.addResource("geolocation");
3161
3162/**
3163 * This class provides access to device GPS data.
3164 * @constructor
3165 */
3166Geolocation = function() {
3167    // The last known GPS position.
3168    this.lastPosition = null;
3169    this.listener = null;
3170    this.timeoutTimerId = 0;
3171
3172};
3173
3174
3175/**
3176 * Asynchronously aquires the current position.
3177 * @param {Function} successCallback The function to call when the position
3178 * data is available
3179 * @param {Function} errorCallback The function to call when there is an error
3180 * getting the position data.
3181 * @param {PositionOptions} options The options for getting the position data
3182 * such as timeout.
3183 * PositionOptions.forcePrompt:Bool default false,
3184 * - tells iPhone to prompt the user to turn on location services.
3185 * - may cause your app to exit while the user is sent to the Settings app
3186 * PositionOptions.distanceFilter:double aka Number
3187 * - used to represent a distance in meters.
3188PositionOptions
3189{
3190   desiredAccuracy:Number
3191   - a distance in meters
3192                < 10   = best accuracy  ( Default value )
3193                < 100  = Nearest Ten Meters
3194                < 1000 = Nearest Hundred Meters
3195                < 3000 = Accuracy Kilometers
3196                3000+  = Accuracy 3 Kilometers
3197               
3198        forcePrompt:Boolean default false ( iPhone Only! )
3199    - tells iPhone to prompt the user to turn on location services.
3200        - may cause your app to exit while the user is sent to the Settings app
3201       
3202        distanceFilter:Number
3203        - The minimum distance (measured in meters) a device must move laterally before an update event is generated.
3204        - measured relative to the previously delivered location
3205        - default value: null ( all movements will be reported )
3206       
3207}
3208
3209 */
3210 
3211Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallback, options) 
3212{
3213    // create an always valid local success callback
3214    var win = successCallback;
3215    if (!win || typeof(win) != 'function')
3216    {
3217        win = function(position) {};
3218    }
3219   
3220    // create an always valid local error callback
3221    var fail = errorCallback;
3222    if (!fail || typeof(fail) != 'function')
3223    {
3224        fail = function(positionError) {};
3225    }   
3226
3227    var self = this;
3228    var totalTime = 0;
3229        var timeoutTimerId;
3230       
3231        // set params to our default values
3232        var params = new PositionOptions();
3233       
3234    if (options) 
3235    {
3236        if (options.maximumAge) 
3237        {
3238            // special case here if we have a cached value that is younger than maximumAge
3239            if(this.lastPosition)
3240            {
3241                var now = new Date().getTime();
3242                if((now - this.lastPosition.timestamp) < options.maximumAge)
3243                {
3244                    win(this.lastPosition); // send cached position immediately
3245                    return;                 // Note, execution stops here -jm
3246                }
3247            }
3248            params.maximumAge = options.maximumAge;
3249        }
3250        if (options.enableHighAccuracy) 
3251        {
3252            params.enableHighAccuracy = (options.enableHighAccuracy == true); // make sure it's truthy
3253        }
3254        if (options.timeout) 
3255        {
3256            params.timeout = options.timeout;
3257        }
3258    }
3259   
3260    this.listener = {"success":win,"fail":fail};
3261    this.start(params);
3262       
3263        var onTimeout = function()
3264        {
3265            self.setError(new PositionError(PositionError.TIMEOUT,"Geolocation Error: Timeout."));
3266        };
3267
3268    clearTimeout(this.timeoutTimerId);
3269    this.timeoutTimerId = setTimeout(onTimeout, params.timeout); 
3270};
3271
3272/**
3273 * Asynchronously aquires the position repeatedly at a given interval.
3274 * @param {Function} successCallback The function to call each time the position
3275 * data is available
3276 * @param {Function} errorCallback The function to call when there is an error
3277 * getting the position data.
3278 * @param {PositionOptions} options The options for getting the position data
3279 * such as timeout and the frequency of the watch.
3280 */
3281Geolocation.prototype.watchPosition = function(successCallback, errorCallback, options) {
3282        // Invoke the appropriate callback with a new Position object every time the implementation
3283        // determines that the position of the hosting device has changed.
3284
3285        var self = this; // those == this & that
3286       
3287        var params = new PositionOptions();
3288
3289    if(options)
3290    {
3291        if (options.maximumAge) {
3292            params.maximumAge = options.maximumAge;
3293        }
3294        if (options.enableHighAccuracy) {
3295            params.enableHighAccuracy = options.enableHighAccuracy;
3296        }
3297        if (options.timeout) {
3298            params.timeout = options.timeout;
3299        }
3300    }
3301
3302        var that = this;
3303    var lastPos = that.lastPosition? that.lastPosition.clone() : null;
3304   
3305        var intervalFunction = function() {
3306       
3307                var filterFun = function(position) {
3308            if (lastPos == null || !position.equals(lastPos)) {
3309                // only call the success callback when there is a change in position, per W3C
3310                successCallback(position);
3311            }
3312           
3313            // clone the new position, save it as our last position (internal var)
3314            lastPos = position.clone();
3315        };
3316               
3317                that.getCurrentPosition(filterFun, errorCallback, params);
3318        };
3319       
3320    // Retrieve location immediately and schedule next retrieval afterwards
3321        intervalFunction();
3322       
3323        return setInterval(intervalFunction, params.timeout);
3324};
3325
3326
3327/**
3328 * Clears the specified position watch.
3329 * @param {String} watchId The ID of the watch returned from #watchPosition.
3330 */
3331Geolocation.prototype.clearWatch = function(watchId) {
3332        clearInterval(watchId);
3333};
3334
3335/**
3336 * Called by the geolocation framework when the current location is found.
3337 * @param {PositionOptions} position The current position.
3338 */
3339Geolocation.prototype.setLocation = function(position) 
3340{
3341    var _position = new Position(position.coords, position.timestamp);
3342
3343    if(this.timeoutTimerId)
3344    {
3345        clearTimeout(this.timeoutTimerId);
3346        this.timeoutTimerId = 0;
3347    }
3348   
3349        this.lastError = null;
3350    this.lastPosition = _position;
3351   
3352    if(this.listener && typeof(this.listener.success) == 'function')
3353    {
3354        this.listener.success(_position);
3355    }
3356   
3357    this.listener = null;
3358};
3359
3360/**
3361 * Called by the geolocation framework when an error occurs while looking up the current position.
3362 * @param {String} message The text of the error message.
3363 */
3364Geolocation.prototype.setError = function(error) 
3365{
3366        var _error = new PositionError(error.code, error.message);
3367       
3368    if(this.timeoutTimerId)
3369    {
3370        clearTimeout(this.timeoutTimerId);
3371        this.timeoutTimerId = 0;
3372    }
3373   
3374    this.lastError = _error;
3375    // call error handlers directly
3376    if(this.listener && typeof(this.listener.fail) == 'function')
3377    {
3378        this.listener.fail(_error);
3379    }
3380    this.listener = null;
3381
3382};
3383
3384Geolocation.prototype.start = function(positionOptions) 
3385{
3386    PhoneGap.exec(null, null, "com.phonegap.geolocation", "startLocation", [positionOptions]);
3387
3388};
3389
3390Geolocation.prototype.stop = function() 
3391{
3392    PhoneGap.exec(null, null, "com.phonegap.geolocation", "stopLocation", []);
3393};
3394
3395
3396PhoneGap.addConstructor(function() 
3397{
3398    if (typeof navigator._geo == "undefined") 
3399    {
3400        // replace origObj's functions ( listed in funkList ) with the same method name on proxyObj
3401        // this is a workaround to prevent UIWebView/MobileSafari default implementation of GeoLocation
3402        // because it includes the full page path as the title of the alert prompt
3403        var __proxyObj = function (origObj,proxyObj,funkList)
3404        {
3405            var replaceFunk = function(org,proxy,fName)
3406            { 
3407                org[fName] = function()
3408                { 
3409                   return proxy[fName].apply(proxy,arguments); 
3410                }; 
3411            };
3412
3413            for(var v in funkList) { replaceFunk(origObj,proxyObj,funkList[v]);}
3414        }
3415        navigator._geo = new Geolocation();
3416        __proxyObj(navigator.geolocation, navigator._geo,
3417                 ["setLocation","getCurrentPosition","watchPosition",
3418                  "clearWatch","setError","start","stop"]);
3419
3420    }
3421
3422});
3423};
3424if (!PhoneGap.hasResource("compass")) {
3425        PhoneGap.addResource("compass");
3426
3427CompassError = function(){
3428   this.code = null;
3429};
3430
3431// Capture error codes
3432CompassError.COMPASS_INTERNAL_ERR = 0;
3433CompassError.COMPASS_NOT_SUPPORTED = 20;
3434
3435CompassHeading = function() {
3436        this.magneticHeading = null;
3437        this.trueHeading = null;
3438        this.headingAccuracy = null;
3439        this.timestamp = null;
3440}       
3441/**
3442 * This class provides access to device Compass data.
3443 * @constructor
3444 */
3445Compass = function() {
3446    /**
3447     * List of compass watch timers
3448     */
3449    this.timers = {};
3450};
3451
3452/**
3453 * Asynchronously acquires the current heading.
3454 * @param {Function} successCallback The function to call when the heading
3455 * data is available
3456 * @param {Function} errorCallback The function to call when there is an error
3457 * getting the heading data.
3458 * @param {PositionOptions} options The options for getting the heading data (not used).
3459 */
3460Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, options) {
3461        // successCallback required
3462    if (typeof successCallback !== "function") {
3463        console.log("Compass Error: successCallback is not a function");
3464        return;
3465    }
3466
3467    // errorCallback optional
3468    if (errorCallback && (typeof errorCallback !== "function")) {
3469        console.log("Compass Error: errorCallback is not a function");
3470        return;
3471    }
3472
3473    // Get heading
3474    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.geolocation", "getCurrentHeading", []);
3475};
3476
3477/**
3478 * Asynchronously acquires the heading repeatedly at a given interval.
3479 * @param {Function} successCallback The function to call each time the heading
3480 * data is available
3481 * @param {Function} errorCallback The function to call when there is an error
3482 * getting the heading data.
3483 * @param {HeadingOptions} options The options for getting the heading data
3484 * such as timeout and the frequency of the watch.
3485 */
3486Compass.prototype.watchHeading= function(successCallback, errorCallback, options) 
3487{
3488        // Default interval (100 msec)
3489    var frequency = (options !== undefined) ? options.frequency : 100;
3490
3491    // successCallback required
3492    if (typeof successCallback !== "function") {
3493        console.log("Compass Error: successCallback is not a function");
3494        return;
3495    }
3496
3497    // errorCallback optional
3498    if (errorCallback && (typeof errorCallback !== "function")) {
3499        console.log("Compass Error: errorCallback is not a function");
3500        return;
3501    }
3502
3503    // Start watch timer to get headings
3504    var id = PhoneGap.createUUID();
3505    navigator.compass.timers[id] = setInterval(
3506        function() {
3507            PhoneGap.exec(successCallback, errorCallback, "com.phonegap.geolocation", "getCurrentHeading", [{repeats: 1}]);
3508        }, frequency);
3509
3510    return id;
3511};
3512
3513
3514/**
3515 * Clears the specified heading watch.
3516 * @param {String} watchId The ID of the watch returned from #watchHeading.
3517 */
3518Compass.prototype.clearWatch = function(id) 
3519{
3520        // Stop javascript timer & remove from timer list
3521    if (id && navigator.compass.timers[id]) {
3522        clearInterval(navigator.compass.timers[id]);
3523        delete navigator.compass.timers[id];
3524    }
3525    if (navigator.compass.timers.length == 0) {
3526        // stop the
3527        PhoneGap.exec(null, null, "com.phonegap.geolocation", "stopHeading", []);
3528    }
3529};
3530
3531/** iOS only
3532 * Asynchronously fires when the heading changes from the last reading.  The amount of distance
3533 * required to trigger the event is specified in the filter paramter.
3534 * @param {Function} successCallback The function to call each time the heading
3535 * data is available
3536 * @param {Function} errorCallback The function to call when there is an error
3537 * getting the heading data.
3538 * @param {HeadingOptions} options The options for getting the heading data
3539 *                      @param {filter} number of degrees change to trigger a callback with heading data (float)
3540 *
3541 * In iOS this function is more efficient than calling watchHeading  with a frequency for updates.
3542 * Only one watchHeadingFilter can be in effect at one time.  If a watchHeadingFilter is in effect, calling
3543 * getCurrentHeading or watchHeading will use the existing filter value for specifying heading change.
3544  */
3545Compass.prototype.watchHeadingFilter = function(successCallback, errorCallback, options) 
3546{
3547 
3548        if (options === undefined || options.filter === undefined) {
3549                console.log("Compass Error:  options.filter not specified");
3550                return;
3551        }
3552
3553    // successCallback required
3554    if (typeof successCallback !== "function") {
3555        console.log("Compass Error: successCallback is not a function");
3556        return;
3557    }
3558
3559    // errorCallback optional
3560    if (errorCallback && (typeof errorCallback !== "function")) {
3561        console.log("Compass Error: errorCallback is not a function");
3562        return;
3563    }
3564    PhoneGap.exec(successCallback, errorCallback, "com.phonegap.geolocation", "watchHeadingFilter", [options]);
3565}
3566Compass.prototype.clearWatchFilter = function() 
3567{
3568        PhoneGap.exec(null, null, "com.phonegap.geolocation", "stopHeading", []);
3569};
3570
3571PhoneGap.addConstructor(function() 
3572{
3573    if (typeof navigator.compass == "undefined") 
3574    {
3575        navigator.compass = new Compass();
3576    }
3577});
3578};
3579
3580if (!PhoneGap.hasResource("media")) {
3581        PhoneGap.addResource("media");
3582
3583/*
3584 * PhoneGap is available under *either* the terms of the modified BSD license *or* the
3585 * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
3586 *
3587 * Copyright (c) 2005-2010, Nitobi Software Inc.
3588 * Copyright (c) 2010,2011 IBM Corporation
3589 */
3590
3591/**
3592 * List of media objects.
3593 * PRIVATE
3594 */
3595PhoneGap.mediaObjects = {};
3596
3597/**
3598 * Object that receives native callbacks.
3599 * PRIVATE
3600 */
3601PhoneGap.Media = function() {};
3602
3603
3604/**
3605 * Get the media object.
3606 * PRIVATE
3607 *
3608 * @param id            The media object id (string)
3609 */
3610PhoneGap.Media.getMediaObject = function(id) {
3611    return PhoneGap.mediaObjects[id];
3612};
3613
3614/**
3615 * Audio has status update.
3616 * PRIVATE
3617 *
3618 * @param id            The media object id (string)
3619 * @param msg           The status message (int)
3620 * @param value        The status code (int)
3621 */
3622PhoneGap.Media.onStatus = function(id, msg, value) {
3623    var media = PhoneGap.mediaObjects[id];
3624
3625    // If state update
3626    if (msg == Media.MEDIA_STATE) {
3627        if (value == Media.MEDIA_STOPPED) {
3628            if (media.successCallback) {
3629                media.successCallback();
3630            }
3631        }
3632        if (media.statusCallback) {
3633            media.statusCallback(value);
3634        }
3635    }
3636    else if (msg == Media.MEDIA_DURATION) {
3637        media._duration = value;
3638    }
3639    else if (msg == Media.MEDIA_ERROR) {
3640        if (media.errorCallback) {
3641            media.errorCallback(value);
3642        }
3643    }
3644    else if (msg == Media.MEDIA_POSITION) {
3645        media._position = value;
3646    }
3647};
3648
3649/**
3650 * This class provides access to the device media, interfaces to both sound and video
3651 *
3652 * @param src                   The file name or url to play
3653 * @param successCallback       The callback to be called when the file is done playing or recording.
3654 *                                  successCallback() - OPTIONAL
3655 * @param errorCallback         The callback to be called if there is an error.
3656 *                                  errorCallback(int errorCode) - OPTIONAL
3657 * @param statusCallback        The callback to be called when media status has changed.
3658 *                                  statusCallback(int statusCode) - OPTIONAL
3659 * @param positionCallback      The callback to be called when media position has changed.
3660 *                                  positionCallback(long position) - OPTIONAL
3661 */
3662Media = function(src, successCallback, errorCallback, statusCallback, positionCallback) {
3663
3664    // successCallback optional
3665    if (successCallback && (typeof successCallback != "function")) {
3666        console.log("Media Error: successCallback is not a function");
3667        return;
3668    }
3669
3670    // errorCallback optional
3671    if (errorCallback && (typeof errorCallback != "function")) {
3672        console.log("Media Error: errorCallback is not a function");
3673        return;
3674    }
3675
3676    // statusCallback optional
3677    if (statusCallback && (typeof statusCallback != "function")) {
3678        console.log("Media Error: statusCallback is not a function");
3679        return;
3680    }
3681
3682    // positionCallback optional -- NOT SUPPORTED
3683    if (positionCallback && (typeof positionCallback != "function")) {
3684        console.log("Media Error: positionCallback is not a function");
3685        return;
3686    }
3687
3688    this.id = PhoneGap.createUUID();
3689    PhoneGap.mediaObjects[this.id] = this;
3690    this.src = src;
3691    this.successCallback = successCallback;
3692    this.errorCallback = errorCallback;
3693    this.statusCallback = statusCallback;
3694    this.positionCallback = positionCallback;
3695    this._duration = -1;
3696    this._position = -1;
3697};
3698
3699// Media messages
3700Media.MEDIA_STATE = 1;
3701Media.MEDIA_DURATION = 2;
3702Media.MEDIA_POSITION = 3;
3703Media.MEDIA_ERROR = 9;
3704
3705// Media states
3706Media.MEDIA_NONE = 0;
3707Media.MEDIA_STARTING = 1;
3708Media.MEDIA_RUNNING = 2;
3709Media.MEDIA_PAUSED = 3;
3710Media.MEDIA_STOPPED = 4;
3711Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
3712
3713// TODO: Will MediaError be used?
3714/**
3715 * This class contains information about any Media errors.
3716 * @constructor
3717 */
3718
3719MediaError = function() {
3720        this.code = null,
3721        this.message = "";
3722}
3723
3724
3725MediaError.MEDIA_ERR_ABORTED        = 1;
3726MediaError.MEDIA_ERR_NETWORK        = 2;
3727MediaError.MEDIA_ERR_DECODE         = 3;
3728MediaError.MEDIA_ERR_NONE_SUPPORTED = 4;
3729
3730/**
3731 * Start or resume playing audio file.
3732 */
3733Media.prototype.play = function(options) {
3734    PhoneGap.exec(null, null, "com.phonegap.media", "play", [this.id, this.src, options]);
3735};
3736
3737/**
3738 * Stop playing audio file.
3739 */
3740Media.prototype.stop = function() {
3741    PhoneGap.exec(null, null, "com.phonegap.media","stop", [this.id, this.src]);
3742};
3743
3744/**
3745 * Pause playing audio file.
3746 */
3747Media.prototype.pause = function() {
3748    PhoneGap.exec(null, null, "com.phonegap.media","pause", [this.id, this.src]);
3749};
3750
3751/**
3752 * Seek or jump to a new time in the track..
3753 */
3754Media.prototype.seekTo = function(milliseconds) {
3755    PhoneGap.exec(null, null, "com.phonegap.media", "seekTo", [this.id, this.src, milliseconds]);
3756};
3757
3758/**
3759 * Get duration of an audio file.
3760 * The duration is only set for audio that is playing, paused or stopped.
3761 *
3762 * @return      duration or -1 if not known.
3763 */
3764Media.prototype.getDuration = function() {
3765    return this._duration;
3766};
3767
3768/**
3769 * Get position of audio.
3770 *
3771 * @return
3772 */
3773Media.prototype.getCurrentPosition = function(successCB, errorCB) {
3774        var errCallback = (errorCB == undefined || errorCB == null) ? null : errorCB;
3775    PhoneGap.exec(successCB, errorCB, "com.phonegap.media", "getCurrentPosition", [this.id, this.src]);
3776};
3777
3778// iOS only.  prepare/load the audio in preparation for playing
3779Media.prototype.prepare = function(successCB, errorCB) {
3780        PhoneGap.exec(successCB, errorCB, "com.phonegap.media", "prepare", [this.id, this.src]);
3781}
3782
3783/**
3784 * Start recording audio file.
3785 */
3786Media.prototype.startRecord = function() {
3787    PhoneGap.exec(null, null, "com.phonegap.media","startAudioRecord", [this.id, this.src]);
3788};
3789
3790/**
3791 * Stop recording audio file.
3792 */
3793Media.prototype.stopRecord = function() {
3794    PhoneGap.exec(null, null, "com.phonegap.media","stopAudioRecord", [this.id, this.src]);
3795};
3796
3797/**
3798 * Release the resources.
3799 */
3800Media.prototype.release = function() {
3801    PhoneGap.exec(null, null, "com.phonegap.media","release", [this.id, this.src]);
3802};
3803
3804};
3805if (!PhoneGap.hasResource("notification")) {
3806        PhoneGap.addResource("notification");
3807
3808/**
3809 * This class provides access to notifications on the device.
3810 */
3811Notification = function() {
3812};
3813
3814/**
3815 * Open a native alert dialog, with a customizable title and button text.
3816 *
3817 * @param {String} message              Message to print in the body of the alert
3818 * @param {Function} completeCallback   The callback that is called when user clicks on a button.
3819 * @param {String} title                Title of the alert dialog (default: Alert)
3820 * @param {String} buttonLabel          Label of the close button (default: OK)
3821 */
3822Notification.prototype.alert = function(message, completeCallback, title, buttonLabel) {
3823    var _title = title;
3824    if (title == null || typeof title === 'undefined') {
3825        _title = "Alert";
3826    }
3827    var _buttonLabel = (buttonLabel || "OK");
3828    PhoneGap.exec(completeCallback, null, "com.phonegap.notification", "alert", [message,{ "title": _title, "buttonLabel": _buttonLabel}]);
3829};
3830
3831/**
3832 * Open a native confirm dialog, with a customizable title and button text.
3833 * The result that the user selects is returned to the result callback.
3834 *
3835 * @param {String} message              Message to print in the body of the alert
3836 * @param {Function} resultCallback     The callback that is called when user clicks on a button.
3837 * @param {String} title                Title of the alert dialog (default: Confirm)
3838 * @param {String} buttonLabels         Comma separated list of the labels of the buttons (default: 'OK,Cancel')
3839 */
3840Notification.prototype.confirm = function(message, resultCallback, title, buttonLabels) {
3841    var _title = (title || "Confirm");
3842    var _buttonLabels = (buttonLabels || "OK,Cancel");
3843    this.alert(message, resultCallback, _title, _buttonLabels);
3844};
3845
3846/**
3847 * Causes the device to blink a status LED.
3848 * @param {Integer} count The number of blinks.
3849 * @param {String} colour The colour of the light.
3850 */
3851Notification.prototype.blink = function(count, colour) {
3852// NOT IMPLEMENTED     
3853};
3854
3855Notification.prototype.vibrate = function(mills) {
3856        PhoneGap.exec(null, null, "com.phonegap.notification", "vibrate", []);
3857};
3858
3859Notification.prototype.beep = function(count, volume) {
3860        // No Volume yet for the iphone interface
3861        // We can use a canned beep sound and call that
3862        new Media('beep.wav').play();
3863};
3864
3865PhoneGap.addConstructor(function() {
3866    if (typeof navigator.notification == "undefined") navigator.notification = new Notification();
3867});
3868};
3869if (!PhoneGap.hasResource("orientation")) {
3870        PhoneGap.addResource("orientation");
3871
3872/**
3873 * This class provides access to the device orientation.
3874 * @constructor
3875 */
3876Orientation  = function() {
3877        /**
3878         * The current orientation, or null if the orientation hasn't changed yet.
3879         */
3880        this.currentOrientation = null;
3881}
3882
3883/**
3884 * Set the current orientation of the phone.  This is called from the device automatically.
3885 *
3886 * When the orientation is changed, the DOMEvent \c orientationChanged is dispatched against
3887 * the document element.  The event has the property \c orientation which can be used to retrieve
3888 * the device's current orientation, in addition to the \c Orientation.currentOrientation class property.
3889 *
3890 * @param {Number} orientation The orientation to be set
3891 */
3892Orientation.prototype.setOrientation = function(orientation) {
3893    Orientation.currentOrientation = orientation;
3894    var e = document.createEvent('Events');
3895    e.initEvent('orientationChanged', 'false', 'false');
3896    e.orientation = orientation;
3897    document.dispatchEvent(e);
3898};
3899
3900/**
3901 * Asynchronously aquires the current orientation.
3902 * @param {Function} successCallback The function to call when the orientation
3903 * is known.
3904 * @param {Function} errorCallback The function to call when there is an error
3905 * getting the orientation.
3906 */
3907Orientation.prototype.getCurrentOrientation = function(successCallback, errorCallback) {
3908        // If the position is available then call success
3909        // If the position is not available then call error
3910};
3911
3912/**
3913 * Asynchronously aquires the orientation repeatedly at a given interval.
3914 * @param {Function} successCallback The function to call each time the orientation
3915 * data is available.
3916 * @param {Function} errorCallback The function to call when there is an error
3917 * getting the orientation data.
3918 */
3919Orientation.prototype.watchOrientation = function(successCallback, errorCallback) {
3920        // Invoke the appropriate callback with a new Position object every time the implementation
3921        // determines that the position of the hosting device has changed.
3922        this.getCurrentPosition(successCallback, errorCallback);
3923        return setInterval(function() {
3924                navigator.orientation.getCurrentOrientation(successCallback, errorCallback);
3925        }, 10000);
3926};
3927
3928/**
3929 * Clears the specified orientation watch.
3930 * @param {String} watchId The ID of the watch returned from #watchOrientation.
3931 */
3932Orientation.prototype.clearWatch = function(watchId) {
3933        clearInterval(watchId);
3934};
3935
3936Orientation.install = function()
3937{
3938    if (typeof navigator.orientation == "undefined") { 
3939                navigator.orientation = new Orientation();
3940        }
3941       
3942        var windowDispatchAvailable = !(window.dispatchEvent === undefined); // undefined in iOS 3.x
3943        if (windowDispatchAvailable) {
3944                return;
3945        } 
3946       
3947        // the code below is to capture window.add/remove eventListener calls on window
3948        // this is for iOS 3.x where listening on 'orientationchange' events don't work on document/window (don't know why)
3949        // however, window.onorientationchange DOES handle the 'orientationchange' event (sent through document), so...
3950        // then we multiplex the window.onorientationchange event (consequently - people shouldn't overwrite this)
3951       
3952        var self = this;
3953        var orientationchangeEvent = 'orientationchange';
3954        var newOrientationchangeEvent = 'orientationchange_pg';
3955       
3956        // backup original `window.addEventListener`, `window.removeEventListener`
3957    var _addEventListener = window.addEventListener;
3958    var _removeEventListener = window.removeEventListener;
3959
3960        window.onorientationchange = function() {
3961                PhoneGap.fireEvent(newOrientationchangeEvent, window);
3962        }
3963       
3964    // override `window.addEventListener`
3965    window.addEventListener = function() {
3966        if (arguments[0] === orientationchangeEvent) {
3967                        arguments[0] = newOrientationchangeEvent; 
3968                } 
3969                                                                                                       
3970                if (!windowDispatchAvailable) {
3971                        return document.addEventListener.apply(this, arguments);
3972                } else {
3973                        return _addEventListener.apply(this, arguments);
3974                }
3975    }; 
3976
3977    // override `window.removeEventListener'
3978    window.removeEventListener = function() {
3979        if (arguments[0] === orientationchangeEvent) {
3980                        arguments[0] = newOrientationchangeEvent; 
3981                } 
3982               
3983                if (!windowDispatchAvailable) {
3984                        return document.removeEventListener.apply(this, arguments);
3985                } else {
3986                        return _removeEventListener.apply(this, arguments);
3987                }
3988    }; 
3989};
3990
3991PhoneGap.addConstructor(Orientation.install);
3992
3993};
3994if (!PhoneGap.hasResource("sms")) {
3995        PhoneGap.addResource("sms");
3996
3997/**
3998 * This class provides access to the device SMS functionality.
3999 * @constructor
4000 */
4001Sms = function() {
4002
4003}
4004
4005/**
4006 * Sends an SMS message.
4007 * @param {Integer} number The phone number to send the message to.
4008 * @param {String} message The contents of the SMS message to send.
4009 * @param {Function} successCallback The function to call when the SMS message is sent.
4010 * @param {Function} errorCallback The function to call when there is an error sending the SMS message.
4011 * @param {PositionOptions} options The options for accessing the GPS location such as timeout and accuracy.
4012 */
4013Sms.prototype.send = function(number, message, successCallback, errorCallback, options) {
4014        // not sure why this is here when it does nothing????
4015};
4016
4017PhoneGap.addConstructor(function() {
4018    if (typeof navigator.sms == "undefined") navigator.sms = new Sms();
4019});
4020};
4021if (!PhoneGap.hasResource("telephony")) {
4022        PhoneGap.addResource("telephony");
4023
4024/**
4025 * This class provides access to the telephony features of the device.
4026 * @constructor
4027 */
4028Telephony = function() {
4029       
4030}
4031
4032/**
4033 * Calls the specifed number.
4034 * @param {Integer} number The number to be called.
4035 */
4036Telephony.prototype.call = function(number) {
4037        // not sure why this is here when it does nothing????
4038};
4039
4040PhoneGap.addConstructor(function() {
4041    if (typeof navigator.telephony == "undefined") navigator.telephony = new Telephony();
4042});
4043};if (!PhoneGap.hasResource("network")) {
4044        PhoneGap.addResource("network");
4045
4046// //////////////////////////////////////////////////////////////////
4047
4048Connection = function() {
4049        /*
4050         * One of the connection constants below.
4051         */
4052        this.type = Connection.UNKNOWN;
4053
4054        /* initialize from the extended DeviceInfo properties */
4055    try {     
4056                this.type       = DeviceInfo.connection.type;
4057    } 
4058        catch(e) {
4059    }
4060};
4061
4062Connection.UNKNOWN = "unknown"; // Unknown connection type
4063Connection.ETHERNET = "ethernet";
4064Connection.WIFI = "wifi";
4065Connection.CELL_2G = "2g"; // the default for iOS, for any cellular connection
4066Connection.CELL_3G = "3g";
4067Connection.CELL_4G = "4g";
4068Connection.NONE = "none"; // NO connectivity
4069
4070
4071PhoneGap.addConstructor(function() {
4072    if (typeof navigator.network == "undefined") navigator.network = {};
4073    if (typeof navigator.network.connection == "undefined") navigator.network.connection = new Connection();
4074});
4075
4076};if (!PhoneGap.hasResource("splashscreen")) {
4077        PhoneGap.addResource("splashscreen");
4078
4079/**
4080 * This class provides access to the splashscreen
4081 */
4082SplashScreen = function() {
4083};
4084
4085SplashScreen.prototype.show = function() {
4086    PhoneGap.exec(null, null, "com.phonegap.splashscreen", "show", []);
4087};
4088
4089SplashScreen.prototype.hide = function() {
4090    PhoneGap.exec(null, null, "com.phonegap.splashscreen", "hide", []);
4091};
4092
4093PhoneGap.addConstructor(function() {
4094    if (typeof navigator.splashscreen == "undefined") navigator.splashscreen = new SplashScreen();
4095});
4096
4097};
Note: See TracBrowser for help on using the repository browser.