[tizen SDK samples ]  mobile-spec test app

add a tizen SDK elcipse project generated using codova-2.0.0.js file, config .xml file required, tizen.css& sounds folder

more sample will come in this folder
diff --git a/tizen SDK samples/mobile-spec/.project b/tizen SDK samples/mobile-spec/.project
new file mode 100644
index 0000000..990b36a
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/.project
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>mobile-spec</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.wst.common.project.facet.core.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>json.validation.builder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.tizen.web.jslint.nature.JSLintBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.tizen.web.project.builder.WACBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.tizen.web.css.nature.CSSBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
+		<nature>json.validation.nature</nature>
+		<nature>org.tizen.web.jslint.nature.JSLintNature</nature>
+		<nature>org.tizen.web.WACnature</nature>
+		<nature>org.tizen.web.css.nature.CSSNature</nature>
+		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
+	</natures>
+</projectDescription>
diff --git a/tizen SDK samples/mobile-spec/.settings/.jsdtscope b/tizen SDK samples/mobile-spec/.settings/.jsdtscope
new file mode 100644
index 0000000..c506e0a
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/.settings/.jsdtscope
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path=""/>
+	<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
+	<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="con" path="org.tizen.web.project.initializer.WACLibraryInitializer"/>
+	<classpathentry kind="con" path="org.tizen.web.project.initializer.W3CLibraryInitializer"/>
+	<classpathentry kind="con" path="org.tizen.web.project.initializer.JQuery16LibraryInitializer"/>
+	<classpathentry kind="output" path=""/>
+</classpath>
diff --git a/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.common.component b/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.common.component
new file mode 100644
index 0000000..8cfe2d7
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.common.component
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-modules id="moduleCoreId" project-version="1.5.0">
+    <wb-module deploy-name="mobile-spec">
+        <wb-resource deploy-path="/" source-path="/WebContent"/>
+        <property name="context-root" value="mobile-spec"/>
+    </wb-module>
+</project-modules>
diff --git a/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.common.project.facet.core.xml b/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.common.project.facet.core.xml
new file mode 100644
index 0000000..316995f
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.common.project.facet.core.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<faceted-project>
+  <fixed facet="wst.jsdt.web"/>
+  <installed facet="wst.web" version="1.0"/>
+  <installed facet="wst.jsdt.web" version="1.0"/>
+</faceted-project>
diff --git a/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.css.core.prefs b/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.css.core.prefs
new file mode 100644
index 0000000..15df0c7
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.css.core.prefs
@@ -0,0 +1,3 @@
+#Thu Jul 26 18:10:20 CEST 2012
+css-profile/<project>=org.eclipse.wst.css.core.cssprofile.css3
+eclipse.preferences.version=1
diff --git a/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.jsdt.ui.superType.container b/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.jsdt.ui.superType.container
new file mode 100644
index 0000000..3bd5d0a
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.jsdt.ui.superType.container
@@ -0,0 +1 @@
+org.eclipse.wst.jsdt.launching.baseBrowserLibrary
\ No newline at end of file
diff --git a/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.jsdt.ui.superType.name b/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.jsdt.ui.superType.name
new file mode 100644
index 0000000..05bd71b
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.jsdt.ui.superType.name
@@ -0,0 +1 @@
+Window
\ No newline at end of file
diff --git a/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.validation.prefs b/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.validation.prefs
new file mode 100644
index 0000000..23fdadd
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/.settings/org.eclipse.wst.validation.prefs
@@ -0,0 +1,9 @@
+#Thu Jul 26 18:10:19 CEST 2012
+DELEGATES_PREFERENCE=delegateValidatorList
+USER_BUILD_PREFERENCE=enabledBuildValidatorList
+USER_MANUAL_PREFERENCE=enabledManualValidatorList
+USER_PREFERENCE=overrideGlobalPreferencesfalse
+eclipse.preferences.version=1
+override=true
+suspend=false
+vf.version=3
diff --git a/tizen SDK samples/mobile-spec/404.html b/tizen SDK samples/mobile-spec/404.html
new file mode 100644
index 0000000..5b60299
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/404.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Page Not Found :(</title> 
+  <style>
+	  body { text-align: center;}
+	  h1 { font-size: 50px; text-align: center }
+	  span[frown] { transform: rotate(90deg); display:inline-block; color: #bbb; }
+	  body { font: 20px Constantia, 'Hoefler Text',  "Adobe Caslon Pro", Baskerville, Georgia, Times, serif; color: #999; text-shadow: 2px 2px 2px rgba(200, 200, 200, 0.5); }
+	  ::-moz-selection{ background:#FF5E99; color:#fff; }
+	  ::selection { background:#FF5E99; color:#fff; } 
+	  article {display:block; text-align: left; width: 500px; margin: 0 auto; }
+	  
+	  a { color: rgb(36, 109, 56); text-decoration:none; }
+	  a:hover { color: rgb(96, 73, 141) ; text-shadow: 2px 2px 2px rgba(36, 109, 56, 0.5); }
+  </style>
+</head>
+<body>
+     <article>
+	  <h1>Not found <span frown>:(</span></h1>
+	   <div>
+	       <p>Sorry, but the page you were trying to view does not exist.</p>
+	       <p>It looks like this was the result of either:</p>
+	       <ul>
+		   <li>a mistyped address</li>
+		   <li>an out-of-date link</li>
+	       </ul>
+	   </div>
+	    
+	    <script>
+	    var GOOG_FIXURL_LANG = (navigator.language || '').slice(0,2),
+		GOOG_FIXURL_SITE = location.host;
+	    </script>
+	    <script src="http://linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"></script>
+     </article>
+</body>
+</html>
\ No newline at end of file
diff --git a/tizen SDK samples/mobile-spec/LICENSE b/tizen SDK samples/mobile-spec/LICENSE
new file mode 100644
index 0000000..5652ae1
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/LICENSE
@@ -0,0 +1,208 @@
+The MIT License
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2009-2011 Rob Ellis, Brock Whitten, Brian LeRoux
+   Copyright 2010-2011, IBM Corporation
+   Copyright 2011 Adobe
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
diff --git a/tizen SDK samples/mobile-spec/README.md b/tizen SDK samples/mobile-spec/README.md
new file mode 100644
index 0000000..5bd0b5f
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/README.md
@@ -0,0 +1,29 @@
+## Mobile Spec Suite ##
+
+These specs are designed to run inside the mobile device that implements it - _it will fail in the DESKTOP browser_.
+
+These set of tests is designed to be used with Cordova. You should initialize a fresh Cordova repository for a target platform and then toss these files into the www folder, replacing the
+contents. 
+
+Make sure you include cordova-\*.js in the www folder.  You also need to edit cordova.js to reference the version of cordova-\*.js file you are testing.
+For example, to test with cordova-0.9.6.1, edit the VERSION variable in the cordova.js file as follows:
+
+    var VERSION='0.9.6.1';
+
+This is done so that you don't have to modify every HTML file when you want to test a new version of Cordova.
+
+The goal is to test mobile device functionality inside a mobile browser.
+Where possible, the Cordova API lines up with HTML 5 spec. Maybe down
+the road we could use this spec for parts of HTML 5, too :)
+
+### Requirements ###
+
+Various parts of this test suite communicate with external servers.
+Therefore, when you wrap up the test suite inside a Cordova application,
+make sure you add the following entries to the whitelist!
+
+- audio.ibeat.org
+- cordova-filetransfer.jitsu.com
+- apache.org (with all subdomains)
+- httpssss://example.com (bad protocol necessary)
+
diff --git a/tizen SDK samples/mobile-spec/accelerometer/index.html b/tizen SDK samples/mobile-spec/accelerometer/index.html
new file mode 100755
index 0000000..efa043b
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/accelerometer/index.html
@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    function roundNumber(num) {
+        var dec = 3;
+        var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
+        return result;
+    }
+
+    //-------------------------------------------------------------------------
+    // Acceleration
+    //-------------------------------------------------------------------------
+    var watchAccelId = null;
+    
+    /**
+     * Start watching acceleration
+     */
+    var watchAccel = function() {
+        console.log("watchAccel()");
+
+        // Success callback
+        var success = function(a){
+            document.getElementById('x').innerHTML = roundNumber(a.x);
+            document.getElementById('y').innerHTML = roundNumber(a.y);
+            document.getElementById('z').innerHTML = roundNumber(a.z);
+            console.log("watchAccel success callback");
+        };
+
+        // Fail callback
+        var fail = function(e){
+            console.log("watchAccel fail callback with error code "+e);
+            stopAccel();
+            setAccelStatus(Accelerometer.ERROR_MSG[e]);
+        };
+
+        // Update acceleration every 1 sec
+        var opt = {};
+        opt.frequency = 1000;
+        watchAccelId = navigator.accelerometer.watchAcceleration(success, fail, opt);
+
+        setAccelStatus("Running");
+    };
+
+    /**
+     * Stop watching the acceleration
+     */
+    var stopAccel = function() {
+    	console.log("stopAccel()");
+        setAccelStatus("Stopped");
+        if (watchAccelId) {
+            navigator.accelerometer.clearWatch(watchAccelId);
+            watchAccelId = null;
+        }
+    };
+
+    /**
+     * Get current acceleration
+     */
+    var getAccel = function() {
+        console.log("getAccel()");
+
+        // Stop accel if running
+        stopAccel();
+
+        // Success callback
+        var success = function(a){
+            document.getElementById('x').innerHTML = roundNumber(a.x);
+            document.getElementById('y').innerHTML = roundNumber(a.y);
+            document.getElementById('z').innerHTML = roundNumber(a.z);
+        };
+
+        // Fail callback
+        var fail = function(e){
+            console.log("getAccel fail callback with error code "+e);
+            setAccelStatus(Accelerometer.ERROR_MSG[e]);
+        };
+
+        // Make call
+        var opt = {};
+        navigator.accelerometer.getCurrentAcceleration(success, fail, opt);
+    };
+
+    /**
+     * Set accelerometer status
+     */
+    var setAccelStatus = function(status) {
+        document.getElementById('accel_status').innerHTML = status;
+    };
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        console.log("accelerometer.init()");
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Acceleration</h1>
+    <div id="info">
+        <div id="accel_status">Stopped</div>
+        <div ><table width="100%">
+            <tr><td width="20%">X:</td><td id="x"> </td></tr>
+            <tr><td width="20%">Y:</td><td id="y"> </td></tr>
+            <tr><td width="20%">Z:</td><td id="z"> </td></tr>
+        </table></div>
+    </div>
+
+    <h2>Action</h2>
+    <div class="btn large" onclick="getAccel();">Get Acceleration</div>
+    <div class="btn large" onclick="watchAccel();">Start Watch</div>
+    <div class="btn large" onclick="stopAccel();">Clear Watch</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>      
diff --git a/tizen SDK samples/mobile-spec/apple-touch-icon-114x114-precomposed.png b/tizen SDK samples/mobile-spec/apple-touch-icon-114x114-precomposed.png
new file mode 100644
index 0000000..de2d59f
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/apple-touch-icon-114x114-precomposed.png
Binary files differ
diff --git a/tizen SDK samples/mobile-spec/apple-touch-icon-57x57-precomposed.png b/tizen SDK samples/mobile-spec/apple-touch-icon-57x57-precomposed.png
new file mode 100644
index 0000000..6d2fc39
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/apple-touch-icon-57x57-precomposed.png
Binary files differ
diff --git a/tizen SDK samples/mobile-spec/apple-touch-icon-72x72-precomposed.png b/tizen SDK samples/mobile-spec/apple-touch-icon-72x72-precomposed.png
new file mode 100644
index 0000000..b20e78e
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/apple-touch-icon-72x72-precomposed.png
Binary files differ
diff --git a/tizen SDK samples/mobile-spec/apple-touch-icon-precomposed.png b/tizen SDK samples/mobile-spec/apple-touch-icon-precomposed.png
new file mode 100644
index 0000000..6d2fc39
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/apple-touch-icon-precomposed.png
Binary files differ
diff --git a/tizen SDK samples/mobile-spec/apple-touch-icon.png b/tizen SDK samples/mobile-spec/apple-touch-icon.png
new file mode 100644
index 0000000..6d2fc39
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/apple-touch-icon.png
Binary files differ
diff --git a/tizen SDK samples/mobile-spec/audio/index.html b/tizen SDK samples/mobile-spec/audio/index.html
new file mode 100644
index 0000000..42cfe83
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/audio/index.html
@@ -0,0 +1,394 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Audio Tests</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8"/>
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    //-------------------------------------------------------------------------
+    // Audio player
+    //-------------------------------------------------------------------------
+    var media1 = null;
+    var media1Timer = null;
+    var audioSrc = null;
+    var recordSrc = "myRecording.mp3";
+
+    /**
+     * Play audio
+     */
+    function playAudio(url) {
+        console.log("playAudio()");
+        console.log(" -- media="+media1);
+
+        //var src = "http://neuga.s3.amazonaws.com/onclassical/strings-or gan.mp3";
+       var src = "http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3";
+        //var src = "/android_asset/www/Jet_Sledding.mp4"; // no work
+        //var src = "http://vprbbc.streamguys.net/vprbbc24.mp3"; // mp3 streaming
+        
+        if (url) {
+            src = url;
+        }
+
+        // Stop playing if src is different from currently playing source
+        if (src != audioSrc) {
+            if (media1 != null) {
+                stopAudio();
+                media1 = null;
+            }
+        }
+
+        if (media1 == null) {
+
+
+            // TEST STREAMING AUDIO PLAYBACK
+            //var src = "http://nunzioweb.com/misc/Bon_Jovi-Crush_Mystery_Train.mp3";   // works
+            //var src = "http://nunzioweb.com/misc/Bon_Jovi-Crush_Mystery_Train.m3u"; // doesn't work
+            //var src = "http://www.wav-sounds.com/cartoon/bugsbunny1.wav"; // works
+            //var src = "http://www.angelfire.com/fl5/html-tutorial/a/couldyou.mid"; // doesn't work
+            //var src = "MusicSearch/mp3/train.mp3";    // works
+            //var src = "bryce.mp3";  // works
+            //var src = "/android_asset/www/bryce.mp3"; // works
+
+            media1 = new Media(src,
+                function() {
+                    console.log("playAudio():Audio Success");
+                },
+                function(err) {
+                    console.log("playAudio():Audio Error: "+err.code);
+                    setAudioStatus("Error: " + err.code);
+                },
+                function(status) {
+                    console.log("playAudio():Audio Status: "+status);
+                    setAudioStatus(Media.MEDIA_MSG[status]);
+
+                    // If stopped, then stop getting current position
+                    if (Media.MEDIA_STOPPED == status) {
+                        clearInterval(media1Timer);
+                        media1Timer = null;
+                        setAudioPosition("0 sec");
+                    }
+                });
+        }
+        audioSrc = src;
+        document.getElementById('audio_duration').innerHTML = "";
+        // Play audio
+        media1.play();
+        if (media1Timer == null && media1.getCurrentPosition) {
+            media1Timer = setInterval(
+                function() {
+                    media1.getCurrentPosition(
+                        function(position) {
+                            console.log("Pos="+position);
+                            if (position >= 0.0) {
+                                setAudioPosition(position+" sec");
+                            }
+                        },
+                        function(e) {
+                            console.log("Error getting pos="+e);
+                            setAudioPosition("Error: "+e);
+                        }
+                    );
+                },
+                1000
+            );
+        }
+
+        // Get duration
+        var counter = 0;
+        var timerDur = setInterval(
+            function() {
+                counter = counter + 100;
+                if (counter > 2000) {
+                    clearInterval(timerDur);
+                }
+                var dur = media1.getDuration();
+                if (dur > 0) {
+                    clearInterval(timerDur);
+                    document.getElementById('audio_duration').innerHTML = dur + " sec";
+                }
+            }, 100);
+    }
+
+    /**
+     * Pause audio playback
+     */
+    function pauseAudio() {
+        console.log("pauseAudio()");
+        if (media1) {
+            media1.pause();
+        }
+    }
+
+    /**
+     * Stop audio
+     */
+    function stopAudio() {
+        console.log("stopAudio()");
+        if (media1) {
+            media1.stop();
+            media1.release();
+        }
+        clearInterval(media1Timer);
+        media1Timer = null;
+    }
+
+    /**
+     * Set audio status
+     */
+    var setAudioStatus = function(status) {
+        document.getElementById('audio_status').innerHTML = status;
+    };
+
+    /**
+     * Set audio position
+     */
+    var setAudioPosition = function(position) {
+        document.getElementById('audio_position').innerHTML = position;
+    };
+
+    //-------------------------------------------------------------------------
+    // Audio recorder
+    //-------------------------------------------------------------------------
+    var mediaRec = null;
+    var recTime = 0;
+
+    /**
+     * Record audio
+     */
+    function recordAudio() {
+        console.log("recordAudio()");
+        console.log(" -- media="+mediaRec);
+        if (mediaRec == null) {
+
+            var src = recordSrc;
+            mediaRec = new Media(src,
+                    function() {
+                        console.log("recordAudio():Audio Success");
+                    },
+                    function(err) {
+                        console.log("recordAudio():Audio Error: "+err.code);
+                        setAudioStatus("Error: " + err.code);
+                    },
+                    function(status) {
+                        console.log("recordAudio():Audio Status: "+status);
+                        setAudioStatus(Media.MEDIA_MSG[status]);
+                    }
+                );
+        }
+
+        navigator.notification.beep(1);
+
+        // Record audio
+        mediaRec.startRecord();
+
+        // Stop recording after 10 sec
+        recTime = 0;
+        var recInterval = setInterval(function() {
+            recTime = recTime + 1;
+            setAudioPosition(recTime+" sec");
+            if (recTime >= 10) {
+                clearInterval(recInterval);
+                if (mediaRec.stopAudioRecord){
+                    mediaRec.stopAudioRecord();
+                } else {
+                    mediaRec.stopRecord();
+                }
+                console.log("recordAudio(): stop");
+                navigator.notification.beep(1);
+            }
+        }, 1000);
+    }
+
+    /**
+     * Play back recorded audio
+     */
+    function playRecording() {
+        playAudio(recordSrc);
+    }
+    
+    /**
+     * Function to create a file for iOS recording
+     */
+    function getRecordSrc() {
+        var fsFail = function(error) {
+            console.log("error creating file for iOS recording");
+        };
+        var gotFile = function(file) {
+            recordSrc = file.fullPath;
+            //console.log("recording Src: " + recordSrc);
+        };
+        var gotFS = function(fileSystem) {
+            fileSystem.root.getFile("iOSRecording.wav", {create: true}, gotFile, fsFail);
+        };
+        window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, gotFS, fsFail);
+    }
+    
+    /**
+     * Function to create a file for BB recording
+     */
+    function getRecordSrcBB() {
+        var fsFail = function(error) {
+            console.log("error creating file for BB recording");
+        };
+        var gotFile = function(file) {
+            recordSrc = file.fullPath;
+        };
+        var gotFS = function(fileSystem) {
+            fileSystem.root.getFile("BBRecording.amr", {create: true}, gotFile, fsFail);
+        };
+        window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, gotFS, fsFail);
+    }
+
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                if (device.platform.indexOf("iPhone") !=-1 || device.platform.indexOf("iPad") !=-1)
+                {
+                     getRecordSrc();
+                } else if (typeof blackberry !== 'undefined') {
+                    getRecordSrcBB();
+                }
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+    
+    /**
+     * for forced updates of position after a successful seek
+     */
+    function updatePosition() {
+        media1.getCurrentPosition(
+            function(position) {
+                console.log("Pos="+position);
+                if (position >= 0.0) {
+                    setAudioPosition(position+" sec");
+                }
+            },
+            function(e) {
+                console.log("Error getting pos="+e);
+                setAudioPosition("Error: "+e);
+            });
+    }
+
+    /**
+     *
+     */
+    function seekAudio(mode) {
+        var time = document.getElementById("seekinput").value;
+        if (time == "") {
+            time = 5000;
+        } else {
+            time = time * 1000; //we expect the input to be in seconds
+        }
+        if (media1 == null) {
+            console.log("seekTo requested while media1 is null");
+            if (audioSrc == null) {
+                audioSrc = "http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3";
+            }
+            media1 = new Media(audioSrc,
+                function() {
+                    console.log("seekToAudio():Audio Success");
+                },
+                function(err) {
+                    console.log("seekAudio():Audio Error: "+err.code);
+                    setAudioStatus("Error: " + err.code);
+                },
+                function(status) {
+                    console.log("seekAudio():Audio Status: "+status);
+                    setAudioStatus(Media.MEDIA_MSG[status]);
+
+                    // If stopped, then stop getting current position
+                    if (Media.MEDIA_STOPPED == status) {
+                        clearInterval(media1Timer);
+                        media1Timer = null;
+                        setAudioPosition("0 sec");
+                    }
+                });
+        }
+        
+        media1.getCurrentPosition(
+            function (position) {
+                var deltat = time;
+                if (mode == "by") {
+                    deltat = time + position * 1000;   
+                }
+                media1.seekTo(deltat,
+                    function () {
+                        console.log("seekAudioTo():Audio Success");
+                        //force an update on the position display
+                        updatePosition();
+                    },
+                    function (err) {
+                        console.log("seekAudioTo():Audio Error: " + err.code);
+                    });
+            },
+            function(e) {
+                console.log("Error getting pos="+e);
+                setAudioPosition("Error: "+e);
+            });
+    }
+    
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Audio</h1>  
+    <div id="info">
+        <table width="100%">
+        <tr><td><b>Status:</b></td><td id="audio_status"> </td></tr>
+        <tr><td><b>Duration:</b></td><td id="audio_duration"></td></tr>
+        <tr><td><b>Position:</b></td><td id="audio_position"></td></tr>
+        </table>
+    </div>
+    <h2>Action</h2>
+    <table>
+        <tr>
+            <th colspan=3>Play Sample Audio</th>
+        </tr>
+        <tr>
+            <td><div class="btn large" style="width:100%;" onclick="playAudio();">Play</div></td>
+            <td><div class="btn large" style="width:100%;" onclick="pauseAudio();">Pause</div></td>
+            <td><div class="btn large" style="width:100%;" onclick="stopAudio();">Stop</div></td>
+        </tr>
+        <tr>
+            <td><div class="btn large" style="width:100%;" onclick="seekAudio('by');">Seek By</div></td>
+            <td><div class="btn large" style="width:100%;" onclick="seekAudio('to');">Seek To</div></td>
+            <td>
+                <div style="width:100%;">
+                    <input class="input numeric" type="number" id="seekinput" value="in seconds">
+                </div>
+            </td>
+            <td><h2>s</h2></td>
+        </tr>
+        <tr>
+            <th colspan=3><br><br>Record Audio</th>
+        </tr>
+        <tr>
+            <td colspan=3><div class="btn large" onclick="recordAudio();">Record Audio for 10 sec</a></td>
+        </tr>
+        <tr>
+            <td><div class="btn large" style="width:100%;" onclick="playRecording();">Play</div></td>
+            <td><div class="btn large" style="width:100%;" onclick="pauseAudio();">Pause</div></td>
+            <td><div class="btn large" style="width:100%;" onclick="stopAudio();">Stop</div></td>
+        </tr>
+    </table>
+    
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+    
+  </body>
+</html>      
diff --git a/tizen SDK samples/mobile-spec/autotest/html/HtmlReporter.js b/tizen SDK samples/mobile-spec/autotest/html/HtmlReporter.js
new file mode 100644
index 0000000..7d9d924
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/html/HtmlReporter.js
@@ -0,0 +1,101 @@
+jasmine.HtmlReporter = function(_doc) {
+  var self = this;
+  var doc = _doc || window.document;
+
+  var reporterView;
+
+  var dom = {};
+
+  // Jasmine Reporter Public Interface
+  self.logRunningSpecs = false;
+
+  self.reportRunnerStarting = function(runner) {
+    var specs = runner.specs() || [];
+
+    if (specs.length == 0) {
+      return;
+    }
+
+    createReporterDom(runner.env.versionString());
+    doc.body.appendChild(dom.reporter);
+
+    reporterView = new jasmine.HtmlReporter.ReporterView(dom);
+    reporterView.addSpecs(specs, self.specFilter);
+  };
+
+  self.reportRunnerResults = function(runner) {
+    reporterView && reporterView.complete();
+  };
+
+  self.reportSuiteResults = function(suite) {
+    reporterView.suiteComplete(suite);
+  };
+
+  self.reportSpecStarting = function(spec) {
+    if (self.logRunningSpecs) {
+      self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
+    }
+  };
+
+  self.reportSpecResults = function(spec) {
+    reporterView.specComplete(spec);
+  };
+
+  self.log = function() {
+    var console = jasmine.getGlobal().console;
+    if (console && console.log) {
+      if (console.log.apply) {
+        console.log.apply(console, arguments);
+      } else {
+        console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
+      }
+    }
+  };
+
+  self.specFilter = function(spec) {
+    if (!focusedSpecName()) {
+      return true;
+    }
+
+    return spec.getFullName().indexOf(focusedSpecName()) === 0;
+  };
+
+  return self;
+
+  function focusedSpecName() {
+    var specName;
+
+    (function memoizeFocusedSpec() {
+      if (specName) {
+        return;
+      }
+
+      var paramMap = [];
+      var params = doc.location.search.substring(1).split('&');
+
+      for (var i = 0; i < params.length; i++) {
+        var p = params[i].split('=');
+        paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
+      }
+
+      specName = paramMap.spec;
+    })();
+
+    return specName;
+  }
+
+  function createReporterDom(version) {
+    dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
+      dom.banner = self.createDom('div', { className: 'banner' },
+        self.createDom('span', { className: 'title' }, "Jasmine "),
+        self.createDom('span', { className: 'version' }, version)),
+
+      dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
+      dom.alert = self.createDom('div', {className: 'alert'}),
+      dom.results = self.createDom('div', {className: 'results'},
+        dom.summary = self.createDom('div', { className: 'summary' }),
+        dom.details = self.createDom('div', { id: 'details' }))
+    );
+  }
+};
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
diff --git a/tizen SDK samples/mobile-spec/autotest/html/HtmlReporterHelpers.js b/tizen SDK samples/mobile-spec/autotest/html/HtmlReporterHelpers.js
new file mode 100644
index 0000000..745e1e0
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/html/HtmlReporterHelpers.js
@@ -0,0 +1,60 @@
+jasmine.HtmlReporterHelpers = {};
+
+jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
+  var el = document.createElement(type);
+
+  for (var i = 2; i < arguments.length; i++) {
+    var child = arguments[i];
+
+    if (typeof child === 'string') {
+      el.appendChild(document.createTextNode(child));
+    } else {
+      if (child) {
+        el.appendChild(child);
+      }
+    }
+  }
+
+  for (var attr in attrs) {
+    if (attr == "className") {
+      el[attr] = attrs[attr];
+    } else {
+      el.setAttribute(attr, attrs[attr]);
+    }
+  }
+
+  return el;
+};
+
+jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
+  var results = child.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.skipped) {
+    status = 'skipped';
+  }
+
+  return status;
+};
+
+jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
+  var parentDiv = this.dom.summary;
+  var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
+  var parent = child[parentSuite];
+
+  if (parent) {
+    if (typeof this.views.suites[parent.id] == 'undefined') {
+      this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
+    }
+    parentDiv = this.views.suites[parent.id].element;
+  }
+
+  parentDiv.appendChild(childElement);
+};
+
+
+jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
+  for(var fn in jasmine.HtmlReporterHelpers) {
+    ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
+  }
+};
+
diff --git a/tizen SDK samples/mobile-spec/autotest/html/ReporterView.js b/tizen SDK samples/mobile-spec/autotest/html/ReporterView.js
new file mode 100644
index 0000000..6a6d005
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/html/ReporterView.js
@@ -0,0 +1,164 @@
+jasmine.HtmlReporter.ReporterView = function(dom) {
+  this.startedAt = new Date();
+  this.runningSpecCount = 0;
+  this.completeSpecCount = 0;
+  this.passedCount = 0;
+  this.failedCount = 0;
+  this.skippedCount = 0;
+
+  this.createResultsMenu = function() {
+    this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
+      this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
+      ' | ',
+      this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
+
+    this.summaryMenuItem.onclick = function() {
+      dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
+    };
+
+    this.detailsMenuItem.onclick = function() {
+      showDetails();
+    };
+  };
+
+  this.addSpecs = function(specs, specFilter) {
+    this.totalSpecCount = specs.length;
+
+    this.views = {
+      specs: {},
+      suites: {}
+    };
+
+    for (var i = 0; i < specs.length; i++) {
+      var spec = specs[i];
+      this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
+      if (specFilter(spec)) {
+        this.runningSpecCount++;
+      }
+    }
+  };
+
+  this.specComplete = function(spec) {
+    this.completeSpecCount++;
+
+    if (isUndefined(this.views.specs[spec.id])) {
+      this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
+    }
+
+    var specView = this.views.specs[spec.id];
+
+    switch (specView.status()) {
+      case 'passed':
+        this.passedCount++;
+        break;
+
+      case 'failed':
+        this.failedCount++;
+        break;
+
+      case 'skipped':
+        this.skippedCount++;
+        break;
+    }
+
+    specView.refresh();
+    this.refresh();
+  };
+
+  this.suiteComplete = function(suite) {
+    var suiteView = this.views.suites[suite.id];
+    if (isUndefined(suiteView)) {
+      return;
+    }
+    suiteView.refresh();
+  };
+
+  this.refresh = function() {
+
+    if (isUndefined(this.resultsMenu)) {
+      this.createResultsMenu();
+    }
+
+    // currently running UI
+    if (isUndefined(this.runningAlert)) {
+      this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
+      dom.alert.appendChild(this.runningAlert);
+    }
+    this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
+
+    // skipped specs UI
+    if (isUndefined(this.skippedAlert)) {
+      this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
+    }
+
+    this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
+
+    if (this.skippedCount === 1 && isDefined(dom.alert)) {
+      dom.alert.appendChild(this.skippedAlert);
+    }
+
+    // passing specs UI
+    if (isUndefined(this.passedAlert)) {
+      this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
+    }
+    this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
+
+    // failing specs UI
+    if (isUndefined(this.failedAlert)) {
+      this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
+    }
+    this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
+
+    if (this.failedCount === 1 && isDefined(dom.alert)) {
+      dom.alert.appendChild(this.failedAlert);
+      dom.alert.appendChild(this.resultsMenu);
+    }
+
+    // summary info
+    this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
+    this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
+  };
+
+  this.complete = function() {
+    dom.alert.removeChild(this.runningAlert);
+
+    this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
+
+    if (this.failedCount === 0) {
+      dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
+    } else {
+      showDetails();
+    }
+
+    dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
+  };
+
+  return this;
+
+  function showDetails() {
+    if (dom.reporter.className.search(/showDetails/) === -1) {
+      dom.reporter.className += " showDetails";
+    }
+  }
+
+  function isUndefined(obj) {
+    return typeof obj === 'undefined';
+  }
+
+  function isDefined(obj) {
+    return !isUndefined(obj);
+  }
+
+  function specPluralizedFor(count) {
+    var str = count + " spec";
+    if (count > 1) {
+      str += "s"
+    }
+    return str;
+  }
+
+};
+
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
+
+
diff --git a/tizen SDK samples/mobile-spec/autotest/html/SpecView.js b/tizen SDK samples/mobile-spec/autotest/html/SpecView.js
new file mode 100644
index 0000000..8769bb8
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/html/SpecView.js
@@ -0,0 +1,79 @@
+jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
+  this.spec = spec;
+  this.dom = dom;
+  this.views = views;
+
+  this.symbol = this.createDom('li', { className: 'pending' });
+  this.dom.symbolSummary.appendChild(this.symbol);
+
+  this.summary = this.createDom('div', { className: 'specSummary' },
+      this.createDom('a', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
+        title: this.spec.getFullName()
+      }, this.spec.description)
+  );
+
+  this.detail = this.createDom('div', { className: 'specDetail' },
+      this.createDom('a', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
+        title: this.spec.getFullName()
+      }, this.spec.getFullName())
+  );
+};
+
+jasmine.HtmlReporter.SpecView.prototype.status = function() {
+  return this.getSpecStatus(this.spec);
+};
+
+jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
+  this.symbol.className = this.status();
+
+  switch (this.status()) {
+    case 'skipped':
+      break;
+
+    case 'passed':
+      this.appendSummaryToSuiteDiv();
+      break;
+
+    case 'failed':
+      this.appendSummaryToSuiteDiv();
+      this.appendFailureDetail();
+      break;
+  }
+};
+
+jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
+  this.summary.className += ' ' + this.status();
+  this.appendToSummary(this.spec, this.summary);
+};
+
+jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
+  this.detail.className += ' ' + this.status();
+
+  var resultItems = this.spec.results().getItems();
+  var messagesDiv = this.createDom('div', { className: 'messages' });
+
+  for (var i = 0; i < resultItems.length; i++) {
+    var result = resultItems[i];
+
+    if (result.type == 'log') {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
+    } else if (result.type == 'expect' && result.passed && !result.passed()) {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
+
+      if (result.trace.stack) {
+        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
+      }
+    }
+  }
+
+  if (messagesDiv.childNodes.length > 0) {
+    this.detail.appendChild(messagesDiv);
+    this.dom.details.appendChild(this.detail);
+  }
+};
+
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);
\ No newline at end of file
diff --git a/tizen SDK samples/mobile-spec/autotest/html/SuiteView.js b/tizen SDK samples/mobile-spec/autotest/html/SuiteView.js
new file mode 100644
index 0000000..19a1efa
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/html/SuiteView.js
@@ -0,0 +1,22 @@
+jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
+  this.suite = suite;
+  this.dom = dom;
+  this.views = views;
+
+  this.element = this.createDom('div', { className: 'suite' },
+      this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
+  );
+
+  this.appendToSummary(this.suite, this.element);
+};
+
+jasmine.HtmlReporter.SuiteView.prototype.status = function() {
+  return this.getSpecStatus(this.suite);
+};
+
+jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
+  this.element.className += " " + this.status();
+};
+
+jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
+
diff --git a/tizen SDK samples/mobile-spec/autotest/html/TrivialReporter.js b/tizen SDK samples/mobile-spec/autotest/html/TrivialReporter.js
new file mode 100644
index 0000000..167ac50
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/html/TrivialReporter.js
@@ -0,0 +1,192 @@
+/* @deprecated Use jasmine.HtmlReporter instead
+ */
+jasmine.TrivialReporter = function(doc) {
+  this.document = doc || document;
+  this.suiteDivs = {};
+  this.logRunningSpecs = false;
+};
+
+jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
+  var el = document.createElement(type);
+
+  for (var i = 2; i < arguments.length; i++) {
+    var child = arguments[i];
+
+    if (typeof child === 'string') {
+      el.appendChild(document.createTextNode(child));
+    } else {
+      if (child) { el.appendChild(child); }
+    }
+  }
+
+  for (var attr in attrs) {
+    if (attr == "className") {
+      el[attr] = attrs[attr];
+    } else {
+      el.setAttribute(attr, attrs[attr]);
+    }
+  }
+
+  return el;
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
+  var showPassed, showSkipped;
+
+  this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
+      this.createDom('div', { className: 'banner' },
+        this.createDom('div', { className: 'logo' },
+            this.createDom('span', { className: 'title' }, "Jasmine"),
+            this.createDom('span', { className: 'version' }, runner.env.versionString())),
+        this.createDom('div', { className: 'options' },
+            "Show ",
+            showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
+            showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
+            )
+          ),
+
+      this.runnerDiv = this.createDom('div', { className: 'runner running' },
+          this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
+          this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
+          this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
+      );
+
+  this.document.body.appendChild(this.outerDiv);
+
+  var suites = runner.suites();
+  for (var i = 0; i < suites.length; i++) {
+    var suite = suites[i];
+    var suiteDiv = this.createDom('div', { className: 'suite' },
+        this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
+        this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
+    this.suiteDivs[suite.id] = suiteDiv;
+    var parentDiv = this.outerDiv;
+    if (suite.parentSuite) {
+      parentDiv = this.suiteDivs[suite.parentSuite.id];
+    }
+    parentDiv.appendChild(suiteDiv);
+  }
+
+  this.startedAt = new Date();
+
+  var self = this;
+  showPassed.onclick = function(evt) {
+    if (showPassed.checked) {
+      self.outerDiv.className += ' show-passed';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
+    }
+  };
+
+  showSkipped.onclick = function(evt) {
+    if (showSkipped.checked) {
+      self.outerDiv.className += ' show-skipped';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
+    }
+  };
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
+  var results = runner.results();
+  var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
+  this.runnerDiv.setAttribute("class", className);
+  //do it twice for IE
+  this.runnerDiv.setAttribute("className", className);
+  var specs = runner.specs();
+  var specCount = 0;
+  for (var i = 0; i < specs.length; i++) {
+    if (this.specFilter(specs[i])) {
+      specCount++;
+    }
+  }
+  var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
+  message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
+  this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
+
+  this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
+};
+
+jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
+  var results = suite.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.totalCount === 0) { // todo: change this to check results.skipped
+    status = 'skipped';
+  }
+  this.suiteDivs[suite.id].className += " " + status;
+};
+
+jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
+  if (this.logRunningSpecs) {
+    this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
+  }
+};
+
+jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
+  var results = spec.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.skipped) {
+    status = 'skipped';
+  }
+  var specDiv = this.createDom('div', { className: 'spec '  + status },
+      this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
+      this.createDom('a', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(spec.getFullName()),
+        title: spec.getFullName()
+      }, spec.description));
+
+
+  var resultItems = results.getItems();
+  var messagesDiv = this.createDom('div', { className: 'messages' });
+  for (var i = 0; i < resultItems.length; i++) {
+    var result = resultItems[i];
+
+    if (result.type == 'log') {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
+    } else if (result.type == 'expect' && result.passed && !result.passed()) {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
+
+      if (result.trace.stack) {
+        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
+      }
+    }
+  }
+
+  if (messagesDiv.childNodes.length > 0) {
+    specDiv.appendChild(messagesDiv);
+  }
+
+  this.suiteDivs[spec.suite.id].appendChild(specDiv);
+};
+
+jasmine.TrivialReporter.prototype.log = function() {
+  var console = jasmine.getGlobal().console;
+  if (console && console.log) {
+    if (console.log.apply) {
+      console.log.apply(console, arguments);
+    } else {
+      console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
+    }
+  }
+};
+
+jasmine.TrivialReporter.prototype.getLocation = function() {
+  return this.document.location;
+};
+
+jasmine.TrivialReporter.prototype.specFilter = function(spec) {
+  var paramMap = {};
+  var params = this.getLocation().search.substring(1).split('&');
+  for (var i = 0; i < params.length; i++) {
+    var p = params[i].split('=');
+    paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
+  }
+
+  if (!paramMap.spec) {
+    return true;
+  }
+  return spec.getFullName().indexOf(paramMap.spec) === 0;
+};
diff --git a/tizen SDK samples/mobile-spec/autotest/index.html b/tizen SDK samples/mobile-spec/autotest/index.html
new file mode 100755
index 0000000..70a1eeb
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/index.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
+    <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+
+    <title>Cordova API Specs</title>
+
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" src="../cordova.js"></script>
+  </head>
+  <body id="stage" class="theme">
+    <h1>Cordova API Specs</h1>
+
+    <a href="pages/all.html" class="btn large" style="width:100%;">Run All Tests</a>
+    <a href="pages/accelerometer.html" class="btn large" style="width:100%;">Run Accelerometer Tests</a>
+    <a href="pages/battery.html" class="btn large" style="width:100%;">Run Battery Tests</a>
+    <a href="pages/camera.html" class="btn large" style="width:100%;">Run Camera Tests</a>
+    <a href="pages/capture.html" class="btn large" style="width:100%;">Run Capture Tests</a>
+    <a href="pages/compass.html" class="btn large" style="width:100%;">Run Compass Tests</a>
+    <a href="pages/contacts.html" class="btn large" style="width:100%;">Run Contacts Tests</a>
+    <a href="pages/device.html" class="btn large" style="width:100%;">Run Device Tests</a>
+    <a href="pages/file.html" class="btn large" style="width:100%;">Run File Tests</a>
+    <a href="pages/filetransfer.html" class="btn large" style="width:100%;">Run FileTransfer Tests</a>
+    <a href="pages/geolocation.html" class="btn large" style="width:100%;">Run Geolocation Tests</a>
+    <a href="pages/media.html" class="btn large" style="width:100%;">Run Media Tests</a>
+    <a href="pages/network.html" class="btn large" style="width:100%;">Run Network Tests</a>
+    <a href="pages/notification.html" class="btn large" style="width:100%;">Run Notification Tests</a>
+    <a href="pages/platform.html" class="btn large" style="width:100%;">Run Platform Tests</a>
+    <a href="pages/storage.html" class="btn large" style="width:100%;">Run Storage Tests</a>
+
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>
diff --git a/tizen SDK samples/mobile-spec/autotest/jasmine.css b/tizen SDK samples/mobile-spec/autotest/jasmine.css
new file mode 100644
index 0000000..826e575
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/jasmine.css
@@ -0,0 +1,81 @@
+body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
+
+#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
+#HTMLReporter a { text-decoration: none; }
+#HTMLReporter a:hover { text-decoration: underline; }
+#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
+#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
+#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
+#HTMLReporter .version { color: #aaaaaa; }
+#HTMLReporter .banner { margin-top: 14px; }
+#HTMLReporter .duration { color: #aaaaaa; float: right; }
+#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
+#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
+#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
+#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
+#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
+#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
+#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
+#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
+#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
+#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
+#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
+#HTMLReporter .runningAlert { background-color: #666666; }
+#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
+#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
+#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
+#HTMLReporter .passingAlert { background-color: #a6b779; }
+#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
+#HTMLReporter .failingAlert { background-color: #cf867e; }
+#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
+#HTMLReporter .results { margin-top: 14px; }
+#HTMLReporter #details { display: none; }
+#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
+#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
+#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
+#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
+#HTMLReporter.showDetails .summary { display: none; }
+#HTMLReporter.showDetails #details { display: block; }
+#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
+#HTMLReporter .summary { margin-top: 14px; }
+#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
+#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
+#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
+#HTMLReporter .description + .suite { margin-top: 0; }
+#HTMLReporter .suite { margin-top: 14px; }
+#HTMLReporter .suite a { color: #333333; }
+#HTMLReporter #details .specDetail { margin-bottom: 28px; }
+#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
+#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
+#HTMLReporter .resultMessage span.result { display: block; }
+#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
+
+#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
+#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
+#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
+#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
+#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
+#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
+#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
+#TrivialReporter .runner.running { background-color: yellow; }
+#TrivialReporter .options { text-align: right; font-size: .8em; }
+#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
+#TrivialReporter .suite .suite { margin: 5px; }
+#TrivialReporter .suite.passed { background-color: #dfd; }
+#TrivialReporter .suite.failed { background-color: #fdd; }
+#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
+#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
+#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
+#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
+#TrivialReporter .spec.skipped { background-color: #bbb; }
+#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
+#TrivialReporter .passed { background-color: #cfc; display: none; }
+#TrivialReporter .failed { background-color: #fbb; }
+#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
+#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
+#TrivialReporter .resultMessage .mismatch { color: black; }
+#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
+#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
+#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
+#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
+#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
diff --git a/tizen SDK samples/mobile-spec/autotest/jasmine.js b/tizen SDK samples/mobile-spec/autotest/jasmine.js
new file mode 100644
index 0000000..bccb66c
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/jasmine.js
@@ -0,0 +1,2530 @@
+var isCommonJS = typeof window == "undefined";
+
+/**
+ * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
+ *
+ * @namespace
+ */
+var jasmine = {};
+if (isCommonJS) exports.jasmine = jasmine;
+/**
+ * @private
+ */
+jasmine.unimplementedMethod_ = function() {
+  throw new Error("unimplemented method");
+};
+
+/**
+ * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
+ * a plain old variable and may be redefined by somebody else.
+ *
+ * @private
+ */
+jasmine.undefined = jasmine.___undefined___;
+
+/**
+ * Show diagnostic messages in the console if set to true
+ *
+ */
+jasmine.VERBOSE = false;
+
+/**
+ * Default interval in milliseconds for event loop yields (e.g. to allow network activity or to refresh the screen with the HTML-based runner). Small values here may result in slow test running. Zero means no updates until all tests have completed.
+ *
+ */
+jasmine.DEFAULT_UPDATE_INTERVAL = 250;
+
+/**
+ * Default timeout interval in milliseconds for waitsFor() blocks.
+ */
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
+
+jasmine.getGlobal = function() {
+  function getGlobal() {
+    return this;
+  }
+
+  return getGlobal();
+};
+
+/**
+ * Allows for bound functions to be compared.  Internal use only.
+ *
+ * @ignore
+ * @private
+ * @param base {Object} bound 'this' for the function
+ * @param name {Function} function to find
+ */
+jasmine.bindOriginal_ = function(base, name) {
+  var original = base[name];
+  if (original.apply) {
+    return function() {
+      return original.apply(base, arguments);
+    };
+  } else {
+    // IE support
+    return jasmine.getGlobal()[name];
+  }
+};
+
+jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
+jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
+jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
+jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
+
+jasmine.MessageResult = function(values) {
+  this.type = 'log';
+  this.values = values;
+  this.trace = new Error(); // todo: test better
+};
+
+jasmine.MessageResult.prototype.toString = function() {
+  var text = "";
+  for (var i = 0; i < this.values.length; i++) {
+    if (i > 0) text += " ";
+    if (jasmine.isString_(this.values[i])) {
+      text += this.values[i];
+    } else {
+      text += jasmine.pp(this.values[i]);
+    }
+  }
+  return text;
+};
+
+jasmine.ExpectationResult = function(params) {
+  this.type = 'expect';
+  this.matcherName = params.matcherName;
+  this.passed_ = params.passed;
+  this.expected = params.expected;
+  this.actual = params.actual;
+  this.message = this.passed_ ? 'Passed.' : params.message;
+
+  var trace = (params.trace || new Error(this.message));
+  this.trace = this.passed_ ? '' : trace;
+};
+
+jasmine.ExpectationResult.prototype.toString = function () {
+  return this.message;
+};
+
+jasmine.ExpectationResult.prototype.passed = function () {
+  return this.passed_;
+};
+
+/**
+ * Getter for the Jasmine environment. Ensures one gets created
+ */
+jasmine.getEnv = function() {
+  var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
+  return env;
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isArray_ = function(value) {
+  return jasmine.isA_("Array", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isString_ = function(value) {
+  return jasmine.isA_("String", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isNumber_ = function(value) {
+  return jasmine.isA_("Number", value);
+};
+
+/**
+ * @ignore
+ * @private
+ * @param {String} typeName
+ * @param value
+ * @returns {Boolean}
+ */
+jasmine.isA_ = function(typeName, value) {
+  return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
+};
+
+/**
+ * Pretty printer for expecations.  Takes any object and turns it into a human-readable string.
+ *
+ * @param value {Object} an object to be outputted
+ * @returns {String}
+ */
+jasmine.pp = function(value) {
+  var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
+  stringPrettyPrinter.format(value);
+  return stringPrettyPrinter.string;
+};
+
+/**
+ * Returns true if the object is a DOM Node.
+ *
+ * @param {Object} obj object to check
+ * @returns {Boolean}
+ */
+jasmine.isDomNode = function(obj) {
+  return obj.nodeType > 0;
+};
+
+/**
+ * Returns a matchable 'generic' object of the class type.  For use in expecations of type when values don't matter.
+ *
+ * @example
+ * // don't care about which function is passed in, as long as it's a function
+ * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
+ *
+ * @param {Class} clazz
+ * @returns matchable object of the type clazz
+ */
+jasmine.any = function(clazz) {
+  return new jasmine.Matchers.Any(clazz);
+};
+
+/**
+ * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
+ * attributes on the object.
+ *
+ * @example
+ * // don't care about any other attributes than foo.
+ * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
+ *
+ * @param sample {Object} sample
+ * @returns matchable object for the sample
+ */
+jasmine.objectContaining = function (sample) {
+    return new jasmine.Matchers.ObjectContaining(sample);
+};
+
+/**
+ * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
+ *
+ * Spies should be created in test setup, before expectations.  They can then be checked, using the standard Jasmine
+ * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
+ *
+ * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
+ *
+ * Spies are torn down at the end of every spec.
+ *
+ * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
+ *
+ * @example
+ * // a stub
+ * var myStub = jasmine.createSpy('myStub');  // can be used anywhere
+ *
+ * // spy example
+ * var foo = {
+ *   not: function(bool) { return !bool; }
+ * }
+ *
+ * // actual foo.not will not be called, execution stops
+ * spyOn(foo, 'not');
+
+ // foo.not spied upon, execution will continue to implementation
+ * spyOn(foo, 'not').andCallThrough();
+ *
+ * // fake example
+ * var foo = {
+ *   not: function(bool) { return !bool; }
+ * }
+ *
+ * // foo.not(val) will return val
+ * spyOn(foo, 'not').andCallFake(function(value) {return value;});
+ *
+ * // mock example
+ * foo.not(7 == 7);
+ * expect(foo.not).toHaveBeenCalled();
+ * expect(foo.not).toHaveBeenCalledWith(true);
+ *
+ * @constructor
+ * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
+ * @param {String} name
+ */
+jasmine.Spy = function(name) {
+  /**
+   * The name of the spy, if provided.
+   */
+  this.identity = name || 'unknown';
+  /**
+   *  Is this Object a spy?
+   */
+  this.isSpy = true;
+  /**
+   * The actual function this spy stubs.
+   */
+  this.plan = function() {
+  };
+  /**
+   * Tracking of the most recent call to the spy.
+   * @example
+   * var mySpy = jasmine.createSpy('foo');
+   * mySpy(1, 2);
+   * mySpy.mostRecentCall.args = [1, 2];
+   */
+  this.mostRecentCall = {};
+
+  /**
+   * Holds arguments for each call to the spy, indexed by call count
+   * @example
+   * var mySpy = jasmine.createSpy('foo');
+   * mySpy(1, 2);
+   * mySpy(7, 8);
+   * mySpy.mostRecentCall.args = [7, 8];
+   * mySpy.argsForCall[0] = [1, 2];
+   * mySpy.argsForCall[1] = [7, 8];
+   */
+  this.argsForCall = [];
+  this.calls = [];
+};
+
+/**
+ * Tells a spy to call through to the actual implemenatation.
+ *
+ * @example
+ * var foo = {
+ *   bar: function() { // do some stuff }
+ * }
+ *
+ * // defining a spy on an existing property: foo.bar
+ * spyOn(foo, 'bar').andCallThrough();
+ */
+jasmine.Spy.prototype.andCallThrough = function() {
+  this.plan = this.originalValue;
+  return this;
+};
+
+/**
+ * For setting the return value of a spy.
+ *
+ * @example
+ * // defining a spy from scratch: foo() returns 'baz'
+ * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
+ *
+ * // defining a spy on an existing property: foo.bar() returns 'baz'
+ * spyOn(foo, 'bar').andReturn('baz');
+ *
+ * @param {Object} value
+ */
+jasmine.Spy.prototype.andReturn = function(value) {
+  this.plan = function() {
+    return value;
+  };
+  return this;
+};
+
+/**
+ * For throwing an exception when a spy is called.
+ *
+ * @example
+ * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
+ * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
+ *
+ * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
+ * spyOn(foo, 'bar').andThrow('baz');
+ *
+ * @param {String} exceptionMsg
+ */
+jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
+  this.plan = function() {
+    throw exceptionMsg;
+  };
+  return this;
+};
+
+/**
+ * Calls an alternate implementation when a spy is called.
+ *
+ * @example
+ * var baz = function() {
+ *   // do some stuff, return something
+ * }
+ * // defining a spy from scratch: foo() calls the function baz
+ * var foo = jasmine.createSpy('spy on foo').andCall(baz);
+ *
+ * // defining a spy on an existing property: foo.bar() calls an anonymnous function
+ * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
+ *
+ * @param {Function} fakeFunc
+ */
+jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
+  this.plan = fakeFunc;
+  return this;
+};
+
+/**
+ * Resets all of a spy's the tracking variables so that it can be used again.
+ *
+ * @example
+ * spyOn(foo, 'bar');
+ *
+ * foo.bar();
+ *
+ * expect(foo.bar.callCount).toEqual(1);
+ *
+ * foo.bar.reset();
+ *
+ * expect(foo.bar.callCount).toEqual(0);
+ */
+jasmine.Spy.prototype.reset = function() {
+  this.wasCalled = false;
+  this.callCount = 0;
+  this.argsForCall = [];
+  this.calls = [];
+  this.mostRecentCall = {};
+};
+
+jasmine.createSpy = function(name) {
+
+  var spyObj = function() {
+    spyObj.wasCalled = true;
+    spyObj.callCount++;
+    var args = jasmine.util.argsToArray(arguments);
+    spyObj.mostRecentCall.object = this;
+    spyObj.mostRecentCall.args = args;
+    spyObj.argsForCall.push(args);
+    spyObj.calls.push({object: this, args: args});
+    return spyObj.plan.apply(this, arguments);
+  };
+
+  var spy = new jasmine.Spy(name);
+
+  for (var prop in spy) {
+    spyObj[prop] = spy[prop];
+  }
+
+  spyObj.reset();
+
+  return spyObj;
+};
+
+/**
+ * Determines whether an object is a spy.
+ *
+ * @param {jasmine.Spy|Object} putativeSpy
+ * @returns {Boolean}
+ */
+jasmine.isSpy = function(putativeSpy) {
+  return putativeSpy && putativeSpy.isSpy;
+};
+
+/**
+ * Creates a more complicated spy: an Object that has every property a function that is a spy.  Used for stubbing something
+ * large in one call.
+ *
+ * @param {String} baseName name of spy class
+ * @param {Array} methodNames array of names of methods to make spies
+ */
+jasmine.createSpyObj = function(baseName, methodNames) {
+  if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {
+    throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
+  }
+  var obj = {};
+  for (var i = 0; i < methodNames.length; i++) {
+    obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
+  }
+  return obj;
+};
+
+/**
+ * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
+ *
+ * Be careful not to leave calls to <code>jasmine.log</code> in production code.
+ */
+jasmine.log = function() {
+  var spec = jasmine.getEnv().currentSpec;
+  spec.log.apply(spec, arguments);
+};
+
+/**
+ * Function that installs a spy on an existing object's method name.  Used within a Spec to create a spy.
+ *
+ * @example
+ * // spy example
+ * var foo = {
+ *   not: function(bool) { return !bool; }
+ * }
+ * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
+ *
+ * @see jasmine.createSpy
+ * @param obj
+ * @param methodName
+ * @returns a Jasmine spy that can be chained with all spy methods
+ */
+var spyOn = function(obj, methodName) {
+  return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
+};
+if (isCommonJS) exports.spyOn = spyOn;
+
+/**
+ * Creates a Jasmine spec that will be added to the current suite.
+ *
+ * // TODO: pending tests
+ *
+ * @example
+ * it('should be true', function() {
+ *   expect(true).toEqual(true);
+ * });
+ *
+ * @param {String} desc description of this specification
+ * @param {Function} func defines the preconditions and expectations of the spec
+ */
+var it = function(desc, func) {
+  return jasmine.getEnv().it(desc, func);
+};
+if (isCommonJS) exports.it = it;
+
+/**
+ * Creates a <em>disabled</em> Jasmine spec.
+ *
+ * A convenience method that allows existing specs to be disabled temporarily during development.
+ *
+ * @param {String} desc description of this specification
+ * @param {Function} func defines the preconditions and expectations of the spec
+ */
+var xit = function(desc, func) {
+  return jasmine.getEnv().xit(desc, func);
+};
+if (isCommonJS) exports.xit = xit;
+
+/**
+ * Starts a chain for a Jasmine expectation.
+ *
+ * It is passed an Object that is the actual value and should chain to one of the many
+ * jasmine.Matchers functions.
+ *
+ * @param {Object} actual Actual value to test against and expected value
+ */
+var expect = function(actual) {
+  return jasmine.getEnv().currentSpec.expect(actual);
+};
+if (isCommonJS) exports.expect = expect;
+
+/**
+ * Defines part of a jasmine spec.  Used in cominbination with waits or waitsFor in asynchrnous specs.
+ *
+ * @param {Function} func Function that defines part of a jasmine spec.
+ */
+var runs = function(func) {
+  jasmine.getEnv().currentSpec.runs(func);
+};
+if (isCommonJS) exports.runs = runs;
+
+/**
+ * Waits a fixed time period before moving to the next block.
+ *
+ * @deprecated Use waitsFor() instead
+ * @param {Number} timeout milliseconds to wait
+ */
+var waits = function(timeout) {
+  jasmine.getEnv().currentSpec.waits(timeout);
+};
+if (isCommonJS) exports.waits = waits;
+
+/**
+ * Waits for the latchFunction to return true before proceeding to the next block.
+ *
+ * @param {Function} latchFunction
+ * @param {String} optional_timeoutMessage
+ * @param {Number} optional_timeout
+ */
+var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
+  jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);
+};
+if (isCommonJS) exports.waitsFor = waitsFor;
+
+/**
+ * A function that is called before each spec in a suite.
+ *
+ * Used for spec setup, including validating assumptions.
+ *
+ * @param {Function} beforeEachFunction
+ */
+var beforeEach = function(beforeEachFunction) {
+  jasmine.getEnv().beforeEach(beforeEachFunction);
+};
+if (isCommonJS) exports.beforeEach = beforeEach;
+
+/**
+ * A function that is called after each spec in a suite.
+ *
+ * Used for restoring any state that is hijacked during spec execution.
+ *
+ * @param {Function} afterEachFunction
+ */
+var afterEach = function(afterEachFunction) {
+  jasmine.getEnv().afterEach(afterEachFunction);
+};
+if (isCommonJS) exports.afterEach = afterEach;
+
+/**
+ * Defines a suite of specifications.
+ *
+ * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
+ * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
+ * of setup in some tests.
+ *
+ * @example
+ * // TODO: a simple suite
+ *
+ * // TODO: a simple suite with a nested describe block
+ *
+ * @param {String} description A string, usually the class under test.
+ * @param {Function} specDefinitions function that defines several specs.
+ */
+var describe = function(description, specDefinitions) {
+  return jasmine.getEnv().describe(description, specDefinitions);
+};
+if (isCommonJS) exports.describe = describe;
+
+/**
+ * Disables a suite of specifications.  Used to disable some suites in a file, or files, temporarily during development.
+ *
+ * @param {String} description A string, usually the class under test.
+ * @param {Function} specDefinitions function that defines several specs.
+ */
+var xdescribe = function(description, specDefinitions) {
+  return jasmine.getEnv().xdescribe(description, specDefinitions);
+};
+if (isCommonJS) exports.xdescribe = xdescribe;
+
+
+// Provide the XMLHttpRequest class for IE 5.x-6.x:
+jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
+  function tryIt(f) {
+    try {
+      return f();
+    } catch(e) {
+    }
+    return null;
+  }
+
+  var xhr = tryIt(function() {
+    return new ActiveXObject("Msxml2.XMLHTTP.6.0");
+  }) ||
+    tryIt(function() {
+      return new ActiveXObject("Msxml2.XMLHTTP.3.0");
+    }) ||
+    tryIt(function() {
+      return new ActiveXObject("Msxml2.XMLHTTP");
+    }) ||
+    tryIt(function() {
+      return new ActiveXObject("Microsoft.XMLHTTP");
+    });
+
+  if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");
+
+  return xhr;
+} : XMLHttpRequest;
+/**
+ * @namespace
+ */
+jasmine.util = {};
+
+/**
+ * Declare that a child class inherit it's prototype from the parent class.
+ *
+ * @private
+ * @param {Function} childClass
+ * @param {Function} parentClass
+ */
+jasmine.util.inherit = function(childClass, parentClass) {
+  /**
+   * @private
+   */
+  var subclass = function() {
+  };
+  subclass.prototype = parentClass.prototype;
+  childClass.prototype = new subclass();
+};
+
+jasmine.util.formatException = function(e) {
+  var lineNumber;
+  if (e.line) {
+    lineNumber = e.line;
+  }
+  else if (e.lineNumber) {
+    lineNumber = e.lineNumber;
+  }
+
+  var file;
+
+  if (e.sourceURL) {
+    file = e.sourceURL;
+  }
+  else if (e.fileName) {
+    file = e.fileName;
+  }
+
+  var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();
+
+  if (file && lineNumber) {
+    message += ' in ' + file + ' (line ' + lineNumber + ')';
+  }
+
+  return message;
+};
+
+jasmine.util.htmlEscape = function(str) {
+  if (!str) return str;
+  return str.replace(/&/g, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;');
+};
+
+jasmine.util.argsToArray = function(args) {
+  var arrayOfArgs = [];
+  for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);
+  return arrayOfArgs;
+};
+
+jasmine.util.extend = function(destination, source) {
+  for (var property in source) destination[property] = source[property];
+  return destination;
+};
+
+/**
+ * Environment for Jasmine
+ *
+ * @constructor
+ */
+jasmine.Env = function() {
+  this.currentSpec = null;
+  this.currentSuite = null;
+  this.currentRunner_ = new jasmine.Runner(this);
+
+  this.reporter = new jasmine.MultiReporter();
+
+  this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
+  this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;
+  this.lastUpdate = 0;
+  this.specFilter = function() {
+    return true;
+  };
+
+  this.nextSpecId_ = 0;
+  this.nextSuiteId_ = 0;
+  this.equalityTesters_ = [];
+
+  // wrap matchers
+  this.matchersClass = function() {
+    jasmine.Matchers.apply(this, arguments);
+  };
+  jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
+
+  jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
+};
+
+
+jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
+jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
+jasmine.Env.prototype.setInterval = jasmine.setInterval;
+jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
+
+/**
+ * @returns an object containing jasmine version build info, if set.
+ */
+jasmine.Env.prototype.version = function () {
+  if (jasmine.version_) {
+    return jasmine.version_;
+  } else {
+    throw new Error('Version not set');
+  }
+};
+
+/**
+ * @returns string containing jasmine version build info, if set.
+ */
+jasmine.Env.prototype.versionString = function() {
+  if (!jasmine.version_) {
+    return "version unknown";
+  }
+
+  var version = this.version();
+  var versionString = version.major + "." + version.minor + "." + version.build;
+  if (version.release_candidate) {
+    versionString += ".rc" + version.release_candidate;
+  }
+  versionString += " revision " + version.revision;
+  return versionString;
+};
+
+/**
+ * @returns a sequential integer starting at 0
+ */
+jasmine.Env.prototype.nextSpecId = function () {
+  return this.nextSpecId_++;
+};
+
+/**
+ * @returns a sequential integer starting at 0
+ */
+jasmine.Env.prototype.nextSuiteId = function () {
+  return this.nextSuiteId_++;
+};
+
+/**
+ * Register a reporter to receive status updates from Jasmine.
+ * @param {jasmine.Reporter} reporter An object which will receive status updates.
+ */
+jasmine.Env.prototype.addReporter = function(reporter) {
+  this.reporter.addReporter(reporter);
+};
+
+jasmine.Env.prototype.execute = function() {
+  this.currentRunner_.execute();
+};
+
+jasmine.Env.prototype.describe = function(description, specDefinitions) {
+  var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
+
+  var parentSuite = this.currentSuite;
+  if (parentSuite) {
+    parentSuite.add(suite);
+  } else {
+    this.currentRunner_.add(suite);
+  }
+
+  this.currentSuite = suite;
+
+  var declarationError = null;
+  try {
+    specDefinitions.call(suite);
+  } catch(e) {
+    declarationError = e;
+  }
+
+  if (declarationError) {
+    this.it("encountered a declaration exception", function() {
+      throw declarationError;
+    });
+  }
+
+  this.currentSuite = parentSuite;
+
+  return suite;
+};
+
+jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
+  if (this.currentSuite) {
+    this.currentSuite.beforeEach(beforeEachFunction);
+  } else {
+    this.currentRunner_.beforeEach(beforeEachFunction);
+  }
+};
+
+jasmine.Env.prototype.currentRunner = function () {
+  return this.currentRunner_;
+};
+
+jasmine.Env.prototype.afterEach = function(afterEachFunction) {
+  if (this.currentSuite) {
+    this.currentSuite.afterEach(afterEachFunction);
+  } else {
+    this.currentRunner_.afterEach(afterEachFunction);
+  }
+
+};
+
+jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
+  return {
+    execute: function() {
+    }
+  };
+};
+
+jasmine.Env.prototype.it = function(description, func) {
+  var spec = new jasmine.Spec(this, this.currentSuite, description);
+  this.currentSuite.add(spec);
+  this.currentSpec = spec;
+
+  if (func) {
+    spec.runs(func);
+  }
+
+  return spec;
+};
+
+jasmine.Env.prototype.xit = function(desc, func) {
+  return {
+    id: this.nextSpecId(),
+    runs: function() {
+    }
+  };
+};
+
+jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
+  if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
+    return true;
+  }
+
+  a.__Jasmine_been_here_before__ = b;
+  b.__Jasmine_been_here_before__ = a;
+
+  var hasKey = function(obj, keyName) {
+    return obj !== null && obj[keyName] !== jasmine.undefined;
+  };
+
+  for (var property in b) {
+    if (!hasKey(a, property) && hasKey(b, property)) {
+      mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+    }
+  }
+  for (property in a) {
+    if (!hasKey(b, property) && hasKey(a, property)) {
+      mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
+    }
+  }
+  for (property in b) {
+    if (property == '__Jasmine_been_here_before__') continue;
+    if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
+      mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
+    }
+  }
+
+  if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
+    mismatchValues.push("arrays were not the same length");
+  }
+
+  delete a.__Jasmine_been_here_before__;
+  delete b.__Jasmine_been_here_before__;
+  return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+};
+
+jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
+  mismatchKeys = mismatchKeys || [];
+  mismatchValues = mismatchValues || [];
+
+  for (var i = 0; i < this.equalityTesters_.length; i++) {
+    var equalityTester = this.equalityTesters_[i];
+    var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
+    if (result !== jasmine.undefined) return result;
+  }
+
+  if (a === b) return true;
+
+  if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
+    return (a == jasmine.undefined && b == jasmine.undefined);
+  }
+
+  if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
+    return a === b;
+  }
+
+  if (a instanceof Date && b instanceof Date) {
+    return a.getTime() == b.getTime();
+  }
+
+  if (a.jasmineMatches) {
+    return a.jasmineMatches(b);
+  }
+
+  if (b.jasmineMatches) {
+    return b.jasmineMatches(a);
+  }
+
+  if (a instanceof jasmine.Matchers.ObjectContaining) {
+    return a.matches(b);
+  }
+
+  if (b instanceof jasmine.Matchers.ObjectContaining) {
+    return b.matches(a);
+  }
+
+  if (jasmine.isString_(a) && jasmine.isString_(b)) {
+    return (a == b);
+  }
+
+  if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {
+    return (a == b);
+  }
+
+  if (typeof a === "object" && typeof b === "object") {
+    return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
+  }
+
+  //Straight check
+  return (a === b);
+};
+
+jasmine.Env.prototype.contains_ = function(haystack, needle) {
+  if (jasmine.isArray_(haystack)) {
+    for (var i = 0; i < haystack.length; i++) {
+      if (this.equals_(haystack[i], needle)) return true;
+    }
+    return false;
+  }
+  return haystack.indexOf(needle) >= 0;
+};
+
+jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
+  this.equalityTesters_.push(equalityTester);
+};
+/** No-op base class for Jasmine reporters.
+ *
+ * @constructor
+ */
+jasmine.Reporter = function() {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.reportSpecResults = function(spec) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.Reporter.prototype.log = function(str) {
+};
+
+/**
+ * Blocks are functions with executable code that make up a spec.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {Function} func
+ * @param {jasmine.Spec} spec
+ */
+jasmine.Block = function(env, func, spec) {
+  this.env = env;
+  this.func = func;
+  this.spec = spec;
+};
+
+jasmine.Block.prototype.execute = function(onComplete) {  
+  try {
+    this.func.apply(this.spec);
+  } catch (e) {
+    this.spec.fail(e);
+  }
+  onComplete();
+};
+/** JavaScript API reporter.
+ *
+ * @constructor
+ */
+jasmine.JsApiReporter = function() {
+  this.started = false;
+  this.finished = false;
+  this.suites_ = [];
+  this.results_ = {};
+};
+
+jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
+  this.started = true;
+  var suites = runner.topLevelSuites();
+  for (var i = 0; i < suites.length; i++) {
+    var suite = suites[i];
+    this.suites_.push(this.summarize_(suite));
+  }
+};
+
+jasmine.JsApiReporter.prototype.suites = function() {
+  return this.suites_;
+};
+
+jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
+  var isSuite = suiteOrSpec instanceof jasmine.Suite;
+  var summary = {
+    id: suiteOrSpec.id,
+    name: suiteOrSpec.description,
+    type: isSuite ? 'suite' : 'spec',
+    children: []
+  };
+  
+  if (isSuite) {
+    var children = suiteOrSpec.children();
+    for (var i = 0; i < children.length; i++) {
+      summary.children.push(this.summarize_(children[i]));
+    }
+  }
+  return summary;
+};
+
+jasmine.JsApiReporter.prototype.results = function() {
+  return this.results_;
+};
+
+jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
+  return this.results_[specId];
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
+  this.finished = true;
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
+  this.results_[spec.id] = {
+    messages: spec.results().getItems(),
+    result: spec.results().failedCount > 0 ? "failed" : "passed"
+  };
+};
+
+//noinspection JSUnusedLocalSymbols
+jasmine.JsApiReporter.prototype.log = function(str) {
+};
+
+jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
+  var results = {};
+  for (var i = 0; i < specIds.length; i++) {
+    var specId = specIds[i];
+    results[specId] = this.summarizeResult_(this.results_[specId]);
+  }
+  return results;
+};
+
+jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
+  var summaryMessages = [];
+  var messagesLength = result.messages.length;
+  for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
+    var resultMessage = result.messages[messageIndex];
+    summaryMessages.push({
+      text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
+      passed: resultMessage.passed ? resultMessage.passed() : true,
+      type: resultMessage.type,
+      message: resultMessage.message,
+      trace: {
+        stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined
+      }
+    });
+  }
+
+  return {
+    result : result.result,
+    messages : summaryMessages
+  };
+};
+
+/**
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param actual
+ * @param {jasmine.Spec} spec
+ */
+jasmine.Matchers = function(env, actual, spec, opt_isNot) {
+  this.env = env;
+  this.actual = actual;
+  this.spec = spec;
+  this.isNot = opt_isNot || false;
+  this.reportWasCalled_ = false;
+};
+
+// todo: @deprecated as of Jasmine 0.11, remove soon [xw]
+jasmine.Matchers.pp = function(str) {
+  throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
+};
+
+// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
+jasmine.Matchers.prototype.report = function(result, failing_message, details) {
+  throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
+};
+
+jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
+  for (var methodName in prototype) {
+    if (methodName == 'report') continue;
+    var orig = prototype[methodName];
+    matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
+  }
+};
+
+jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
+  return function() {
+    var matcherArgs = jasmine.util.argsToArray(arguments);
+    var result = matcherFunction.apply(this, arguments);
+
+    if (this.isNot) {
+      result = !result;
+    }
+
+    if (this.reportWasCalled_) return result;
+
+    var message;
+    if (!result) {
+      if (this.message) {
+        message = this.message.apply(this, arguments);
+        if (jasmine.isArray_(message)) {
+          message = message[this.isNot ? 1 : 0];
+        }
+      } else {
+        var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
+        message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;
+        if (matcherArgs.length > 0) {
+          for (var i = 0; i < matcherArgs.length; i++) {
+            if (i > 0) message += ",";
+            message += " " + jasmine.pp(matcherArgs[i]);
+          }
+        }
+        message += ".";
+      }
+    }
+    var expectationResult = new jasmine.ExpectationResult({
+      matcherName: matcherName,
+      passed: result,
+      expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],
+      actual: this.actual,
+      message: message
+    });
+    this.spec.addMatcherResult(expectationResult);
+    return jasmine.undefined;
+  };
+};
+
+
+
+
+/**
+ * toBe: compares the actual to the expected using ===
+ * @param expected
+ */
+jasmine.Matchers.prototype.toBe = function(expected) {
+  return this.actual === expected;
+};
+
+/**
+ * toNotBe: compares the actual to the expected using !==
+ * @param expected
+ * @deprecated as of 1.0. Use not.toBe() instead.
+ */
+jasmine.Matchers.prototype.toNotBe = function(expected) {
+  return this.actual !== expected;
+};
+
+/**
+ * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
+ *
+ * @param expected
+ */
+jasmine.Matchers.prototype.toEqual = function(expected) {
+  return this.env.equals_(this.actual, expected);
+};
+
+/**
+ * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
+ * @param expected
+ * @deprecated as of 1.0. Use not.toEqual() instead.
+ */
+jasmine.Matchers.prototype.toNotEqual = function(expected) {
+  return !this.env.equals_(this.actual, expected);
+};
+
+/**
+ * Matcher that compares the actual to the expected using a regular expression.  Constructs a RegExp, so takes
+ * a pattern or a String.
+ *
+ * @param expected
+ */
+jasmine.Matchers.prototype.toMatch = function(expected) {
+  return new RegExp(expected).test(this.actual);
+};
+
+/**
+ * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
+ * @param expected
+ * @deprecated as of 1.0. Use not.toMatch() instead.
+ */
+jasmine.Matchers.prototype.toNotMatch = function(expected) {
+  return !(new RegExp(expected).test(this.actual));
+};
+
+/**
+ * Matcher that compares the actual to jasmine.undefined.
+ */
+jasmine.Matchers.prototype.toBeDefined = function() {
+  return (this.actual !== jasmine.undefined);
+};
+
+/**
+ * Matcher that compares the actual to jasmine.undefined.
+ */
+jasmine.Matchers.prototype.toBeUndefined = function() {
+  return (this.actual === jasmine.undefined);
+};
+
+/**
+ * Matcher that compares the actual to null.
+ */
+jasmine.Matchers.prototype.toBeNull = function() {
+  return (this.actual === null);
+};
+
+/**
+ * Matcher that boolean not-nots the actual.
+ */
+jasmine.Matchers.prototype.toBeTruthy = function() {
+  return !!this.actual;
+};
+
+
+/**
+ * Matcher that boolean nots the actual.
+ */
+jasmine.Matchers.prototype.toBeFalsy = function() {
+  return !this.actual;
+};
+
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was called.
+ */
+jasmine.Matchers.prototype.toHaveBeenCalled = function() {
+  if (arguments.length > 0) {
+    throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
+  }
+
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+
+  this.message = function() {
+    return [
+      "Expected spy " + this.actual.identity + " to have been called.",
+      "Expected spy " + this.actual.identity + " not to have been called."
+    ];
+  };
+
+  return this.actual.wasCalled;
+};
+
+/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
+jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was not called.
+ *
+ * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
+ */
+jasmine.Matchers.prototype.wasNotCalled = function() {
+  if (arguments.length > 0) {
+    throw new Error('wasNotCalled does not take arguments');
+  }
+
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+
+  this.message = function() {
+    return [
+      "Expected spy " + this.actual.identity + " to not have been called.",
+      "Expected spy " + this.actual.identity + " to have been called."
+    ];
+  };
+
+  return !this.actual.wasCalled;
+};
+
+/**
+ * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
+ *
+ * @example
+ *
+ */
+jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {
+  var expectedArgs = jasmine.util.argsToArray(arguments);
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+  this.message = function() {
+    if (this.actual.callCount === 0) {
+      // todo: what should the failure message for .not.toHaveBeenCalledWith() be? is this right? test better. [xw]
+      return [
+        "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.",
+        "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."
+      ];
+    } else {
+      return [
+        "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall),
+        "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall)
+      ];
+    }
+  };
+
+  return this.env.contains_(this.actual.argsForCall, expectedArgs);
+};
+
+/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
+jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;
+
+/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
+jasmine.Matchers.prototype.wasNotCalledWith = function() {
+  var expectedArgs = jasmine.util.argsToArray(arguments);
+  if (!jasmine.isSpy(this.actual)) {
+    throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
+  }
+
+  this.message = function() {
+    return [
+      "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",
+      "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"
+    ];
+  };
+
+  return !this.env.contains_(this.actual.argsForCall, expectedArgs);
+};
+
+/**
+ * Matcher that checks that the expected item is an element in the actual Array.
+ *
+ * @param {Object} expected
+ */
+jasmine.Matchers.prototype.toContain = function(expected) {
+  return this.env.contains_(this.actual, expected);
+};
+
+/**
+ * Matcher that checks that the expected item is NOT an element in the actual Array.
+ *
+ * @param {Object} expected
+ * @deprecated as of 1.0. Use not.toContain() instead.
+ */
+jasmine.Matchers.prototype.toNotContain = function(expected) {
+  return !this.env.contains_(this.actual, expected);
+};
+
+jasmine.Matchers.prototype.toBeLessThan = function(expected) {
+  return this.actual < expected;
+};
+
+jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {
+  return this.actual > expected;
+};
+
+/**
+ * Matcher that checks that the expected item is equal to the actual item
+ * up to a given level of decimal precision (default 2).
+ *
+ * @param {Number} expected
+ * @param {Number} precision
+ */
+jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {
+  if (!(precision === 0)) {
+    precision = precision || 2;
+  }
+  var multiplier = Math.pow(10, precision);
+  var actual = Math.round(this.actual * multiplier);
+  expected = Math.round(expected * multiplier);
+  return expected == actual;
+};
+
+/**
+ * Matcher that checks that the expected exception was thrown by the actual.
+ *
+ * @param {String} expected
+ */
+jasmine.Matchers.prototype.toThrow = function(expected) {
+  var result = false;
+  var exception;
+  if (typeof this.actual != 'function') {
+    throw new Error('Actual is not a function');
+  }
+  try {
+    this.actual();
+  } catch (e) {
+    exception = e;
+  }
+  if (exception) {
+    result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
+  }
+
+  var not = this.isNot ? "not " : "";
+
+  this.message = function() {
+    if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {
+      return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
+    } else {
+      return "Expected function to throw an exception.";
+    }
+  };
+
+  return result;
+};
+
+jasmine.Matchers.Any = function(expectedClass) {
+  this.expectedClass = expectedClass;
+};
+
+jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {
+  if (this.expectedClass == String) {
+    return typeof other == 'string' || other instanceof String;
+  }
+
+  if (this.expectedClass == Number) {
+    return typeof other == 'number' || other instanceof Number;
+  }
+
+  if (this.expectedClass == Function) {
+    return typeof other == 'function' || other instanceof Function;
+  }
+
+  if (this.expectedClass == Object) {
+    return typeof other == 'object';
+  }
+
+  return other instanceof this.expectedClass;
+};
+
+jasmine.Matchers.Any.prototype.jasmineToString = function() {
+  return '<jasmine.any(' + this.expectedClass + ')>';
+};
+
+jasmine.Matchers.ObjectContaining = function (sample) {
+  this.sample = sample;
+};
+
+jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {
+  mismatchKeys = mismatchKeys || [];
+  mismatchValues = mismatchValues || [];
+
+  var env = jasmine.getEnv();
+
+  var hasKey = function(obj, keyName) {
+    return obj != null && obj[keyName] !== jasmine.undefined;
+  };
+
+  for (var property in this.sample) {
+    if (!hasKey(other, property) && hasKey(this.sample, property)) {
+      mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
+    }
+    else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {
+      mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual.");
+    }
+  }
+
+  return (mismatchKeys.length === 0 && mismatchValues.length === 0);
+};
+
+jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {
+  return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>";
+};
+// Mock setTimeout, clearTimeout
+// Contributed by Pivotal Computer Systems, www.pivotalsf.com
+
+jasmine.FakeTimer = function() {
+  this.reset();
+
+  var self = this;
+  self.setTimeout = function(funcToCall, millis) {
+    self.timeoutsMade++;
+    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);
+    return self.timeoutsMade;
+  };
+
+  self.setInterval = function(funcToCall, millis) {
+    self.timeoutsMade++;
+    self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);
+    return self.timeoutsMade;
+  };
+
+  self.clearTimeout = function(timeoutKey) {
+    self.scheduledFunctions[timeoutKey] = jasmine.undefined;
+  };
+
+  self.clearInterval = function(timeoutKey) {
+    self.scheduledFunctions[timeoutKey] = jasmine.undefined;
+  };
+
+};
+
+jasmine.FakeTimer.prototype.reset = function() {
+  this.timeoutsMade = 0;
+  this.scheduledFunctions = {};
+  this.nowMillis = 0;
+};
+
+jasmine.FakeTimer.prototype.tick = function(millis) {
+  var oldMillis = this.nowMillis;
+  var newMillis = oldMillis + millis;
+  this.runFunctionsWithinRange(oldMillis, newMillis);
+  this.nowMillis = newMillis;
+};
+
+jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {
+  var scheduledFunc;
+  var funcsToRun = [];
+  for (var timeoutKey in this.scheduledFunctions) {
+    scheduledFunc = this.scheduledFunctions[timeoutKey];
+    if (scheduledFunc != jasmine.undefined &&
+        scheduledFunc.runAtMillis >= oldMillis &&
+        scheduledFunc.runAtMillis <= nowMillis) {
+      funcsToRun.push(scheduledFunc);
+      this.scheduledFunctions[timeoutKey] = jasmine.undefined;
+    }
+  }
+
+  if (funcsToRun.length > 0) {
+    funcsToRun.sort(function(a, b) {
+      return a.runAtMillis - b.runAtMillis;
+    });
+    for (var i = 0; i < funcsToRun.length; ++i) {
+      try {
+        var funcToRun = funcsToRun[i];
+        this.nowMillis = funcToRun.runAtMillis;
+        funcToRun.funcToCall();
+        if (funcToRun.recurring) {
+          this.scheduleFunction(funcToRun.timeoutKey,
+              funcToRun.funcToCall,
+              funcToRun.millis,
+              true);
+        }
+      } catch(e) {
+      }
+    }
+    this.runFunctionsWithinRange(oldMillis, nowMillis);
+  }
+};
+
+jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {
+  this.scheduledFunctions[timeoutKey] = {
+    runAtMillis: this.nowMillis + millis,
+    funcToCall: funcToCall,
+    recurring: recurring,
+    timeoutKey: timeoutKey,
+    millis: millis
+  };
+};
+
+/**
+ * @namespace
+ */
+jasmine.Clock = {
+  defaultFakeTimer: new jasmine.FakeTimer(),
+
+  reset: function() {
+    jasmine.Clock.assertInstalled();
+    jasmine.Clock.defaultFakeTimer.reset();
+  },
+
+  tick: function(millis) {
+    jasmine.Clock.assertInstalled();
+    jasmine.Clock.defaultFakeTimer.tick(millis);
+  },
+
+  runFunctionsWithinRange: function(oldMillis, nowMillis) {
+    jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);
+  },
+
+  scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {
+    jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);
+  },
+
+  useMock: function() {
+    if (!jasmine.Clock.isInstalled()) {
+      var spec = jasmine.getEnv().currentSpec;
+      spec.after(jasmine.Clock.uninstallMock);
+
+      jasmine.Clock.installMock();
+    }
+  },
+
+  installMock: function() {
+    jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;
+  },
+
+  uninstallMock: function() {
+    jasmine.Clock.assertInstalled();
+    jasmine.Clock.installed = jasmine.Clock.real;
+  },
+
+  real: {
+    setTimeout: jasmine.getGlobal().setTimeout,
+    clearTimeout: jasmine.getGlobal().clearTimeout,
+    setInterval: jasmine.getGlobal().setInterval,
+    clearInterval: jasmine.getGlobal().clearInterval
+  },
+
+  assertInstalled: function() {
+    if (!jasmine.Clock.isInstalled()) {
+      throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
+    }
+  },
+
+  isInstalled: function() {
+    return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;
+  },
+
+  installed: null
+};
+jasmine.Clock.installed = jasmine.Clock.real;
+
+//else for IE support
+jasmine.getGlobal().setTimeout = function(funcToCall, millis) {
+  if (jasmine.Clock.installed.setTimeout.apply) {
+    return jasmine.Clock.installed.setTimeout.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.setTimeout(funcToCall, millis);
+  }
+};
+
+jasmine.getGlobal().setInterval = function(funcToCall, millis) {
+  if (jasmine.Clock.installed.setInterval.apply) {
+    return jasmine.Clock.installed.setInterval.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.setInterval(funcToCall, millis);
+  }
+};
+
+jasmine.getGlobal().clearTimeout = function(timeoutKey) {
+  if (jasmine.Clock.installed.clearTimeout.apply) {
+    return jasmine.Clock.installed.clearTimeout.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.clearTimeout(timeoutKey);
+  }
+};
+
+jasmine.getGlobal().clearInterval = function(timeoutKey) {
+  if (jasmine.Clock.installed.clearTimeout.apply) {
+    return jasmine.Clock.installed.clearInterval.apply(this, arguments);
+  } else {
+    return jasmine.Clock.installed.clearInterval(timeoutKey);
+  }
+};
+
+/**
+ * @constructor
+ */
+jasmine.MultiReporter = function() {
+  this.subReporters_ = [];
+};
+jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
+
+jasmine.MultiReporter.prototype.addReporter = function(reporter) {
+  this.subReporters_.push(reporter);
+};
+
+(function() {
+  var functionNames = [
+    "reportRunnerStarting",
+    "reportRunnerResults",
+    "reportSuiteResults",
+    "reportSpecStarting",
+    "reportSpecResults",
+    "log"
+  ];
+  for (var i = 0; i < functionNames.length; i++) {
+    var functionName = functionNames[i];
+    jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
+      return function() {
+        for (var j = 0; j < this.subReporters_.length; j++) {
+          var subReporter = this.subReporters_[j];
+          if (subReporter[functionName]) {
+            subReporter[functionName].apply(subReporter, arguments);
+          }
+        }
+      };
+    })(functionName);
+  }
+})();
+/**
+ * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
+ *
+ * @constructor
+ */
+jasmine.NestedResults = function() {
+  /**
+   * The total count of results
+   */
+  this.totalCount = 0;
+  /**
+   * Number of passed results
+   */
+  this.passedCount = 0;
+  /**
+   * Number of failed results
+   */
+  this.failedCount = 0;
+  /**
+   * Was this suite/spec skipped?
+   */
+  this.skipped = false;
+  /**
+   * @ignore
+   */
+  this.items_ = [];
+};
+
+/**
+ * Roll up the result counts.
+ *
+ * @param result
+ */
+jasmine.NestedResults.prototype.rollupCounts = function(result) {
+  this.totalCount += result.totalCount;
+  this.passedCount += result.passedCount;
+  this.failedCount += result.failedCount;
+};
+
+/**
+ * Adds a log message.
+ * @param values Array of message parts which will be concatenated later.
+ */
+jasmine.NestedResults.prototype.log = function(values) {
+  this.items_.push(new jasmine.MessageResult(values));
+};
+
+/**
+ * Getter for the results: message & results.
+ */
+jasmine.NestedResults.prototype.getItems = function() {
+  return this.items_;
+};
+
+/**
+ * Adds a result, tracking counts (total, passed, & failed)
+ * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
+ */
+jasmine.NestedResults.prototype.addResult = function(result) {
+  if (result.type != 'log') {
+    if (result.items_) {
+      this.rollupCounts(result);
+    } else {
+      this.totalCount++;
+      if (result.passed()) {
+        this.passedCount++;
+      } else {
+        this.failedCount++;
+      }
+    }
+  }
+  this.items_.push(result);
+};
+
+/**
+ * @returns {Boolean} True if <b>everything</b> below passed
+ */
+jasmine.NestedResults.prototype.passed = function() {
+  return this.passedCount === this.totalCount;
+};
+/**
+ * Base class for pretty printing for expectation results.
+ */
+jasmine.PrettyPrinter = function() {
+  this.ppNestLevel_ = 0;
+};
+
+/**
+ * Formats a value in a nice, human-readable string.
+ *
+ * @param value
+ */
+jasmine.PrettyPrinter.prototype.format = function(value) {
+  if (this.ppNestLevel_ > 40) {
+    throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');
+  }
+
+  this.ppNestLevel_++;
+  try {
+    if (value === jasmine.undefined) {
+      this.emitScalar('undefined');
+    } else if (value === null) {
+      this.emitScalar('null');
+    } else if (value === jasmine.getGlobal()) {
+      this.emitScalar('<global>');
+    } else if (value.jasmineToString) {
+      this.emitScalar(value.jasmineToString());
+    } else if (typeof value === 'string') {
+      this.emitString(value);
+    } else if (jasmine.isSpy(value)) {
+      this.emitScalar("spy on " + value.identity);
+    } else if (value instanceof RegExp) {
+      this.emitScalar(value.toString());
+    } else if (typeof value === 'function') {
+      this.emitScalar('Function');
+    } else if (typeof value.nodeType === 'number') {
+      this.emitScalar('HTMLNode');
+    } else if (value instanceof Date) {
+      this.emitScalar('Date(' + value + ')');
+    } else if (value.__Jasmine_been_here_before__) {
+      this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');
+    } else if (jasmine.isArray_(value) || typeof value == 'object') {
+      value.__Jasmine_been_here_before__ = true;
+      if (jasmine.isArray_(value)) {
+        this.emitArray(value);
+      } else {
+        this.emitObject(value);
+      }
+      delete value.__Jasmine_been_here_before__;
+    } else {
+      this.emitScalar(value.toString());
+    }
+  } finally {
+    this.ppNestLevel_--;
+  }
+};
+
+jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {
+  for (var property in obj) {
+    if (property == '__Jasmine_been_here_before__') continue;
+    fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && 
+                                         obj.__lookupGetter__(property) !== null) : false);
+  }
+};
+
+jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;
+jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;
+
+jasmine.StringPrettyPrinter = function() {
+  jasmine.PrettyPrinter.call(this);
+
+  this.string = '';
+};
+jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);
+
+jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {
+  this.append(value);
+};
+
+jasmine.StringPrettyPrinter.prototype.emitString = function(value) {
+  this.append("'" + value + "'");
+};
+
+jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {
+  this.append('[ ');
+  for (var i = 0; i < array.length; i++) {
+    if (i > 0) {
+      this.append(', ');
+    }
+    this.format(array[i]);
+  }
+  this.append(' ]');
+};
+
+jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {
+  var self = this;
+  this.append('{ ');
+  var first = true;
+
+  this.iterateObject(obj, function(property, isGetter) {
+    if (first) {
+      first = false;
+    } else {
+      self.append(', ');
+    }
+
+    self.append(property);
+    self.append(' : ');
+    if (isGetter) {
+      self.append('<getter>');
+    } else {
+      self.format(obj[property]);
+    }
+  });
+
+  this.append(' }');
+};
+
+jasmine.StringPrettyPrinter.prototype.append = function(value) {
+  this.string += value;
+};
+jasmine.Queue = function(env) {
+  this.env = env;
+  this.blocks = [];
+  this.running = false;
+  this.index = 0;
+  this.offset = 0;
+  this.abort = false;
+};
+
+jasmine.Queue.prototype.addBefore = function(block) {
+  this.blocks.unshift(block);
+};
+
+jasmine.Queue.prototype.add = function(block) {
+  this.blocks.push(block);
+};
+
+jasmine.Queue.prototype.insertNext = function(block) {
+  this.blocks.splice((this.index + this.offset + 1), 0, block);
+  this.offset++;
+};
+
+jasmine.Queue.prototype.start = function(onComplete) {
+  this.running = true;
+  this.onComplete = onComplete;
+  this.next_();
+};
+
+jasmine.Queue.prototype.isRunning = function() {
+  return this.running;
+};
+
+jasmine.Queue.LOOP_DONT_RECURSE = true;
+
+jasmine.Queue.prototype.next_ = function() {
+  var self = this;
+  var goAgain = true;
+
+  while (goAgain) {
+    goAgain = false;
+    
+    if (self.index < self.blocks.length && !this.abort) {
+      var calledSynchronously = true;
+      var completedSynchronously = false;
+
+      var onComplete = function () {
+        if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
+          completedSynchronously = true;
+          return;
+        }
+
+        if (self.blocks[self.index].abort) {
+          self.abort = true;
+        }
+
+        self.offset = 0;
+        self.index++;
+
+        var now = new Date().getTime();
+        if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
+          self.env.lastUpdate = now;
+          self.env.setTimeout(function() {
+            self.next_();
+          }, 0);
+        } else {
+          if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
+            goAgain = true;
+          } else {
+            self.next_();
+          }
+        }
+      };
+      self.blocks[self.index].execute(onComplete);
+
+      calledSynchronously = false;
+      if (completedSynchronously) {
+        onComplete();
+      }
+      
+    } else {
+      self.running = false;
+      if (self.onComplete) {
+        self.onComplete();
+      }
+    }
+  }
+};
+
+jasmine.Queue.prototype.results = function() {
+  var results = new jasmine.NestedResults();
+  for (var i = 0; i < this.blocks.length; i++) {
+    if (this.blocks[i].results) {
+      results.addResult(this.blocks[i].results());
+    }
+  }
+  return results;
+};
+
+
+/**
+ * Runner
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ */
+jasmine.Runner = function(env) {
+  var self = this;
+  self.env = env;
+  self.queue = new jasmine.Queue(env);
+  self.before_ = [];
+  self.after_ = [];
+  self.suites_ = [];
+};
+
+jasmine.Runner.prototype.execute = function() {
+  var self = this;
+  if (self.env.reporter.reportRunnerStarting) {
+    self.env.reporter.reportRunnerStarting(this);
+  }
+  self.queue.start(function () {
+    self.finishCallback();
+  });
+};
+
+jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
+  beforeEachFunction.typeName = 'beforeEach';
+  this.before_.splice(0,0,beforeEachFunction);
+};
+
+jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
+  afterEachFunction.typeName = 'afterEach';
+  this.after_.splice(0,0,afterEachFunction);
+};
+
+
+jasmine.Runner.prototype.finishCallback = function() {
+  this.env.reporter.reportRunnerResults(this);
+};
+
+jasmine.Runner.prototype.addSuite = function(suite) {
+  this.suites_.push(suite);
+};
+
+jasmine.Runner.prototype.add = function(block) {
+  if (block instanceof jasmine.Suite) {
+    this.addSuite(block);
+  }
+  this.queue.add(block);
+};
+
+jasmine.Runner.prototype.specs = function () {
+  var suites = this.suites();
+  var specs = [];
+  for (var i = 0; i < suites.length; i++) {
+    specs = specs.concat(suites[i].specs());
+  }
+  return specs;
+};
+
+jasmine.Runner.prototype.suites = function() {
+  return this.suites_;
+};
+
+jasmine.Runner.prototype.topLevelSuites = function() {
+  var topLevelSuites = [];
+  for (var i = 0; i < this.suites_.length; i++) {
+    if (!this.suites_[i].parentSuite) {
+      topLevelSuites.push(this.suites_[i]);
+    }
+  }
+  return topLevelSuites;
+};
+
+jasmine.Runner.prototype.results = function() {
+  return this.queue.results();
+};
+/**
+ * Internal representation of a Jasmine specification, or test.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {jasmine.Suite} suite
+ * @param {String} description
+ */
+jasmine.Spec = function(env, suite, description) {
+  if (!env) {
+    throw new Error('jasmine.Env() required');
+  }
+  if (!suite) {
+    throw new Error('jasmine.Suite() required');
+  }
+  var spec = this;
+  spec.id = env.nextSpecId ? env.nextSpecId() : null;
+  spec.env = env;
+  spec.suite = suite;
+  spec.description = description;
+  spec.queue = new jasmine.Queue(env);
+
+  spec.afterCallbacks = [];
+  spec.spies_ = [];
+
+  spec.results_ = new jasmine.NestedResults();
+  spec.results_.description = description;
+  spec.matchersClass = null;
+};
+
+jasmine.Spec.prototype.getFullName = function() {
+  return this.suite.getFullName() + ' ' + this.description + '.';
+};
+
+
+jasmine.Spec.prototype.results = function() {
+  return this.results_;
+};
+
+/**
+ * All parameters are pretty-printed and concatenated together, then written to the spec's output.
+ *
+ * Be careful not to leave calls to <code>jasmine.log</code> in production code.
+ */
+jasmine.Spec.prototype.log = function() {
+  return this.results_.log(arguments);
+};
+
+jasmine.Spec.prototype.runs = function (func) {
+  var block = new jasmine.Block(this.env, func, this);
+  this.addToQueue(block);
+  return this;
+};
+
+jasmine.Spec.prototype.addToQueue = function (block) {
+  if (this.queue.isRunning()) {
+    this.queue.insertNext(block);
+  } else {
+    this.queue.add(block);
+  }
+};
+
+/**
+ * @param {jasmine.ExpectationResult} result
+ */
+jasmine.Spec.prototype.addMatcherResult = function(result) {
+  this.results_.addResult(result);
+};
+
+jasmine.Spec.prototype.expect = function(actual) {
+  var positive = new (this.getMatchersClass_())(this.env, actual, this);
+  positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
+  return positive;
+};
+
+/**
+ * Waits a fixed time period before moving to the next block.
+ *
+ * @deprecated Use waitsFor() instead
+ * @param {Number} timeout milliseconds to wait
+ */
+jasmine.Spec.prototype.waits = function(timeout) {
+  var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
+  this.addToQueue(waitsFunc);
+  return this;
+};
+
+/**
+ * Waits for the latchFunction to return true before proceeding to the next block.
+ *
+ * @param {Function} latchFunction
+ * @param {String} optional_timeoutMessage
+ * @param {Number} optional_timeout
+ */
+jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
+  var latchFunction_ = null;
+  var optional_timeoutMessage_ = null;
+  var optional_timeout_ = null;
+
+  for (var i = 0; i < arguments.length; i++) {
+    var arg = arguments[i];
+    switch (typeof arg) {
+      case 'function':
+        latchFunction_ = arg;
+        break;
+      case 'string':
+        optional_timeoutMessage_ = arg;
+        break;
+      case 'number':
+        optional_timeout_ = arg;
+        break;
+    }
+  }
+
+  var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
+  this.addToQueue(waitsForFunc);
+  return this;
+};
+
+jasmine.Spec.prototype.fail = function (e) {
+  var expectationResult = new jasmine.ExpectationResult({
+    passed: false,
+    message: e ? jasmine.util.formatException(e) : 'Exception',
+    trace: { stack: e.stack }
+  });
+  this.results_.addResult(expectationResult);
+};
+
+jasmine.Spec.prototype.getMatchersClass_ = function() {
+  return this.matchersClass || this.env.matchersClass;
+};
+
+jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
+  var parent = this.getMatchersClass_();
+  var newMatchersClass = function() {
+    parent.apply(this, arguments);
+  };
+  jasmine.util.inherit(newMatchersClass, parent);
+  jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
+  this.matchersClass = newMatchersClass;
+};
+
+jasmine.Spec.prototype.finishCallback = function() {
+  this.env.reporter.reportSpecResults(this);
+};
+
+jasmine.Spec.prototype.finish = function(onComplete) {
+  this.removeAllSpies();
+  this.finishCallback();
+  if (onComplete) {
+    onComplete();
+  }
+};
+
+jasmine.Spec.prototype.after = function(doAfter) {
+  if (this.queue.isRunning()) {
+    this.queue.add(new jasmine.Block(this.env, doAfter, this));
+  } else {
+    this.afterCallbacks.unshift(doAfter);
+  }
+};
+
+jasmine.Spec.prototype.execute = function(onComplete) {
+  var spec = this;
+  if (!spec.env.specFilter(spec)) {
+    spec.results_.skipped = true;
+    spec.finish(onComplete);
+    return;
+  }
+
+  this.env.reporter.reportSpecStarting(this);
+
+  spec.env.currentSpec = spec;
+
+  spec.addBeforesAndAftersToQueue();
+
+  spec.queue.start(function () {
+    spec.finish(onComplete);
+  });
+};
+
+jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
+  var runner = this.env.currentRunner();
+  var i;
+
+  for (var suite = this.suite; suite; suite = suite.parentSuite) {
+    for (i = 0; i < suite.before_.length; i++) {
+      this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
+    }
+  }
+  for (i = 0; i < runner.before_.length; i++) {
+    this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
+  }
+  for (i = 0; i < this.afterCallbacks.length; i++) {
+    this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
+  }
+  for (suite = this.suite; suite; suite = suite.parentSuite) {
+    for (i = 0; i < suite.after_.length; i++) {
+      this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
+    }
+  }
+  for (i = 0; i < runner.after_.length; i++) {
+    this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
+  }
+};
+
+jasmine.Spec.prototype.explodes = function() {
+  throw 'explodes function should not have been called';
+};
+
+jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
+  if (obj == jasmine.undefined) {
+    throw "spyOn could not find an object to spy upon for " + methodName + "()";
+  }
+
+  if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
+    throw methodName + '() method does not exist';
+  }
+
+  if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
+    throw new Error(methodName + ' has already been spied upon');
+  }
+
+  var spyObj = jasmine.createSpy(methodName);
+
+  this.spies_.push(spyObj);
+  spyObj.baseObj = obj;
+  spyObj.methodName = methodName;
+  spyObj.originalValue = obj[methodName];
+
+  obj[methodName] = spyObj;
+
+  return spyObj;
+};
+
+jasmine.Spec.prototype.removeAllSpies = function() {
+  for (var i = 0; i < this.spies_.length; i++) {
+    var spy = this.spies_[i];
+    spy.baseObj[spy.methodName] = spy.originalValue;
+  }
+  this.spies_ = [];
+};
+
+/**
+ * Internal representation of a Jasmine suite.
+ *
+ * @constructor
+ * @param {jasmine.Env} env
+ * @param {String} description
+ * @param {Function} specDefinitions
+ * @param {jasmine.Suite} parentSuite
+ */
+jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
+  var self = this;
+  self.id = env.nextSuiteId ? env.nextSuiteId() : null;
+  self.description = description;
+  self.queue = new jasmine.Queue(env);
+  self.parentSuite = parentSuite;
+  self.env = env;
+  self.before_ = [];
+  self.after_ = [];
+  self.children_ = [];
+  self.suites_ = [];
+  self.specs_ = [];
+};
+
+jasmine.Suite.prototype.getFullName = function() {
+  var fullName = this.description;
+  for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
+    fullName = parentSuite.description + ' ' + fullName;
+  }
+  return fullName;
+};
+
+jasmine.Suite.prototype.finish = function(onComplete) {
+  this.env.reporter.reportSuiteResults(this);
+  this.finished = true;
+  if (typeof(onComplete) == 'function') {
+    onComplete();
+  }
+};
+
+jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {
+  beforeEachFunction.typeName = 'beforeEach';
+  this.before_.unshift(beforeEachFunction);
+};
+
+jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
+  afterEachFunction.typeName = 'afterEach';
+  this.after_.unshift(afterEachFunction);
+};
+
+jasmine.Suite.prototype.results = function() {
+  return this.queue.results();
+};
+
+jasmine.Suite.prototype.add = function(suiteOrSpec) {
+  this.children_.push(suiteOrSpec);
+  if (suiteOrSpec instanceof jasmine.Suite) {
+    this.suites_.push(suiteOrSpec);
+    this.env.currentRunner().addSuite(suiteOrSpec);
+  } else {
+    this.specs_.push(suiteOrSpec);
+  }
+  this.queue.add(suiteOrSpec);
+};
+
+jasmine.Suite.prototype.specs = function() {
+  return this.specs_;
+};
+
+jasmine.Suite.prototype.suites = function() {
+  return this.suites_;
+};
+
+jasmine.Suite.prototype.children = function() {
+  return this.children_;
+};
+
+jasmine.Suite.prototype.execute = function(onComplete) {
+  var self = this;
+  this.queue.start(function () {
+    self.finish(onComplete);
+  });
+};
+jasmine.WaitsBlock = function(env, timeout, spec) {
+  this.timeout = timeout;
+  jasmine.Block.call(this, env, null, spec);
+};
+
+jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
+
+jasmine.WaitsBlock.prototype.execute = function (onComplete) {
+  if (jasmine.VERBOSE) {
+    this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
+  }
+  this.env.setTimeout(function () {
+    onComplete();
+  }, this.timeout);
+};
+/**
+ * A block which waits for some condition to become true, with timeout.
+ *
+ * @constructor
+ * @extends jasmine.Block
+ * @param {jasmine.Env} env The Jasmine environment.
+ * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
+ * @param {Function} latchFunction A function which returns true when the desired condition has been met.
+ * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
+ * @param {jasmine.Spec} spec The Jasmine spec.
+ */
+jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
+  this.timeout = timeout || env.defaultTimeoutInterval;
+  this.latchFunction = latchFunction;
+  this.message = message;
+  this.totalTimeSpentWaitingForLatch = 0;
+  jasmine.Block.call(this, env, null, spec);
+};
+jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
+
+jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
+
+jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
+  if (jasmine.VERBOSE) {
+    this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
+  }
+  var latchFunctionResult;
+  try {
+    latchFunctionResult = this.latchFunction.apply(this.spec);
+  } catch (e) {
+    this.spec.fail(e);
+    onComplete();
+    return;
+  }
+
+  if (latchFunctionResult) {
+    onComplete();
+  } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
+    var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
+    this.spec.fail({
+      name: 'timeout',
+      message: message
+    });
+
+    this.abort = true;
+    onComplete();
+  } else {
+    this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
+    var self = this;
+    this.env.setTimeout(function() {
+      self.execute(onComplete);
+    }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
+  }
+};
+
+jasmine.version_= {
+  "major": 1,
+  "minor": 2,
+  "build": 0,
+  "revision": 1333310630,
+  "release_candidate": 1
+};
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/accelerometer.html b/tizen SDK samples/mobile-spec/autotest/pages/accelerometer.html
new file mode 100644
index 0000000..bac1836
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/accelerometer.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Cordova: Accelerometer API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/accelerometer.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/all.html b/tizen SDK samples/mobile-spec/autotest/pages/all.html
new file mode 100644
index 0000000..f5ed40a
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/all.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Cordova: API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/accelerometer.tests.js"></script>
+  <script type="text/javascript" src="../tests/battery.tests.js"></script>
+  <script type="text/javascript" src="../tests/capture.tests.js"></script>
+  <script type="text/javascript" src="../tests/compass.tests.js"></script>
+  <script type="text/javascript" src="../tests/contacts.tests.js"></script>
+  <script type="text/javascript" src="../tests/camera.tests.js"></script>
+  <script type="text/javascript" src="../tests/device.tests.js"></script>
+  <script type="text/javascript" src="../tests/file.tests.js"></script>
+  <script type="text/javascript" src="../tests/filetransfer.tests.js"></script>
+  <script type="text/javascript" src="../tests/geolocation.tests.js"></script>
+  <script type="text/javascript" src="../tests/media.tests.js"></script>
+  <script type="text/javascript" src="../tests/network.tests.js"></script>
+  <script type="text/javascript" src="../tests/notification.tests.js"></script>
+  <script type="text/javascript" src="../tests/platform.tests.js"></script>
+  <script type="text/javascript" src="../tests/storage.tests.js"></script>
+
+  <script type="text/javascript">
+      var root, temp_root, persistent_root;
+
+      document.addEventListener('deviceready', function () {
+          // one-time retrieval of the root file system entry
+          var onError = function(e) {
+              console.log('[ERROR] Problem setting up root filesystem for test running! Error to follow.');
+              console.log(JSON.stringify(e));
+          };
+
+          window.requestFileSystem(LocalFileSystem.PERSISTENT, 0,
+              function(fileSystem) {
+                  console.log('File API test Init: Setting PERSISTENT FS.');
+                  root = fileSystem.root; // set in file.tests.js
+                  persistent_root = root;
+
+                  // Once root is set up, fire off tests
+                  var jasmineEnv = jasmine.getEnv();
+                  jasmineEnv.updateInterval = 1000;
+
+                  var htmlReporter = new jasmine.HtmlReporter();
+
+                  jasmineEnv.addReporter(htmlReporter);
+
+                  jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                  };
+
+                  jasmineEnv.execute();
+              }, onError);
+          window.requestFileSystem(LocalFileSystem.TEMPORARY, 0,
+              function(fileSystem) {
+                  console.log('File API test Init: Setting TEMPORARY FS.');
+                  temp_root = fileSystem.root; // set in file.tests.js
+              }, onError);
+      }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/battery.html b/tizen SDK samples/mobile-spec/autotest/pages/battery.html
new file mode 100644
index 0000000..8441950
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/battery.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Cordova: Battery API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/battery.tests.js"></script>
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/camera.html b/tizen SDK samples/mobile-spec/autotest/pages/camera.html
new file mode 100644
index 0000000..e136f27
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/camera.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Camera API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/camera.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/capture.html b/tizen SDK samples/mobile-spec/autotest/pages/capture.html
new file mode 100644
index 0000000..0cb2baf
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/capture.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Capture API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/capture.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/compass.html b/tizen SDK samples/mobile-spec/autotest/pages/compass.html
new file mode 100644
index 0000000..dfd9975
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/compass.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Compass API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/compass.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/contacts.html b/tizen SDK samples/mobile-spec/autotest/pages/contacts.html
new file mode 100644
index 0000000..2575e13
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/contacts.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Contacts API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/contacts.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/device.html b/tizen SDK samples/mobile-spec/autotest/pages/device.html
new file mode 100644
index 0000000..ed25d81
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/device.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Device API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/device.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/file.html b/tizen SDK samples/mobile-spec/autotest/pages/file.html
new file mode 100644
index 0000000..d9e21ca
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/file.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Cordova: File API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/file.tests.js"></script>
+
+  <script type="text/javascript">
+      var root, temp_root, persistent_root;
+
+      document.addEventListener('deviceready', function () {
+          // one-time retrieval of the root file system entry
+          var onError = function(e) {
+              console.log('[ERROR] Problem setting up root filesystem for test running! Error to follow.');
+              console.log(JSON.stringify(e));
+          };
+
+          window.requestFileSystem(LocalFileSystem.PERSISTENT, 0,
+              function(fileSystem) {
+                  console.log('File API test Init: Setting PERSISTENT FS.');
+                  root = fileSystem.root; // set in file.tests.js
+                  persistent_root = root;
+
+                  // Once root is set up, fire off tests
+                  var jasmineEnv = jasmine.getEnv();
+                  jasmineEnv.updateInterval = 1000;
+
+                  var htmlReporter = new jasmine.HtmlReporter();
+
+                  jasmineEnv.addReporter(htmlReporter);
+
+                  jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                  };
+
+                  jasmineEnv.execute();
+              }, onError);
+          window.requestFileSystem(LocalFileSystem.TEMPORARY, 0,
+              function(fileSystem) {
+                  console.log('File API test Init: Setting TEMPORARY FS.');
+                  temp_root = fileSystem.root; // set in file.tests.js
+              }, onError);
+      }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/filetransfer.html b/tizen SDK samples/mobile-spec/autotest/pages/filetransfer.html
new file mode 100644
index 0000000..0fde591
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/filetransfer.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Cordova: File API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/filetransfer.tests.js"></script>
+
+  <script type="text/javascript">
+      var root, temp_root, persistent_root;
+
+      document.addEventListener('deviceready', function () {
+          // one-time retrieval of the root file system entry
+          var onError = function(e) {
+              console.log('[ERROR] Problem setting up root filesystem for test running! Error to follow.');
+              console.log(JSON.stringify(e));
+          };
+
+          window.requestFileSystem(LocalFileSystem.PERSISTENT, 0,
+              function(fileSystem) {
+                  console.log('File API test Init: Setting PERSISTENT FS.');
+                  root = fileSystem.root; // set in file.tests.js
+                  persistent_root = root;
+
+                  // Once root is set up, fire off tests
+                  var jasmineEnv = jasmine.getEnv();
+                  jasmineEnv.updateInterval = 1000;
+
+                  var htmlReporter = new jasmine.HtmlReporter();
+
+                  jasmineEnv.addReporter(htmlReporter);
+
+                  jasmineEnv.specFilter = function(spec) {
+                    return htmlReporter.specFilter(spec);
+                  };
+
+                  jasmineEnv.execute();
+              }, onError);
+          window.requestFileSystem(LocalFileSystem.TEMPORARY, 0,
+              function(fileSystem) {
+                  console.log('File API test Init: Setting TEMPORARY FS.');
+                  temp_root = fileSystem.root; // set in file.tests.js
+              }, onError);
+      }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/geolocation.html b/tizen SDK samples/mobile-spec/autotest/pages/geolocation.html
new file mode 100644
index 0000000..578c6f4
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/geolocation.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Geolocation API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/geolocation.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/media.html b/tizen SDK samples/mobile-spec/autotest/pages/media.html
new file mode 100644
index 0000000..48d9e2d
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/media.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Media API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/media.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/network.html b/tizen SDK samples/mobile-spec/autotest/pages/network.html
new file mode 100644
index 0000000..627320c
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/network.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Network API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/network.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/notification.html b/tizen SDK samples/mobile-spec/autotest/pages/notification.html
new file mode 100644
index 0000000..ef9d4a2
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/notification.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Notification API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/notification.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/platform.html b/tizen SDK samples/mobile-spec/autotest/pages/platform.html
new file mode 100644
index 0000000..884ba45
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/platform.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Platform API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/platform.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+
diff --git a/tizen SDK samples/mobile-spec/autotest/pages/storage.html b/tizen SDK samples/mobile-spec/autotest/pages/storage.html
new file mode 100644
index 0000000..eb0703b
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/pages/storage.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>Cordova: Storage API Specs</title>
+
+  <meta name="viewport" content="width=device-width, height=device-height, user-scalable=yes, initial-scale=1.0;" />
+  <!-- Load jasmine -->
+  <link href="../jasmine.css" rel="stylesheet"/>
+  <script type="text/javascript" src="../jasmine.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporterHelpers.js"></script>
+  <script type="text/javascript" src="../html/HtmlReporter.js"></script>
+  <script type="text/javascript" src="../html/ReporterView.js"></script>
+  <script type="text/javascript" src="../html/SpecView.js"></script>
+  <script type="text/javascript" src="../html/SuiteView.js"></script>
+  <script type="text/javascript" src="../html/TrivialReporter.js"></script>
+
+  <!-- Source -->
+  <script type="text/javascript" src="../../cordova.js"></script>
+
+  <!-- Load Test Runner -->
+  <script type="text/javascript" src="../test-runner.js"></script>
+
+  <!-- Tests -->
+  <script type="text/javascript" src="../tests/storage.tests.js"></script>
+
+  <script type="text/javascript">
+    document.addEventListener('deviceready', function () {
+      var jasmineEnv = jasmine.getEnv();
+      jasmineEnv.updateInterval = 1000;
+
+      var htmlReporter = new jasmine.HtmlReporter();
+
+      jasmineEnv.addReporter(htmlReporter);
+
+      jasmineEnv.specFilter = function(spec) {
+        return htmlReporter.specFilter(spec);
+      };
+
+      jasmineEnv.execute();
+    }, false);
+  </script>
+</head>
+
+<body>
+  <a href="javascript:" class="backBtn" onclick="backHome();">Back</a>
+</body>
+</html>
+
diff --git a/tizen SDK samples/mobile-spec/autotest/test-runner.js b/tizen SDK samples/mobile-spec/autotest/test-runner.js
new file mode 100644
index 0000000..b8a9417
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/test-runner.js
@@ -0,0 +1,9 @@
+if (window.sessionStorage != null) {
+    window.sessionStorage.clear();
+}
+
+// Timeout is 2 seconds to allow physical devices enough
+// time to query the response. This is important for some
+// Android devices.
+var Tests = function() {};
+Tests.TEST_TIMEOUT = 7500;
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/accelerometer.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/accelerometer.tests.js
new file mode 100644
index 0000000..0b61ac3
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/accelerometer.tests.js
@@ -0,0 +1,193 @@
+describe('Accelerometer (navigator.accelerometer)', function () {
+    it("should exist", function () {
+        expect(navigator.accelerometer).toBeDefined();
+    });
+
+    describe("getCurrentAcceleration", function() {
+        it("should exist", function() {
+            expect(typeof navigator.accelerometer.getCurrentAcceleration).toBeDefined();
+            expect(typeof navigator.accelerometer.getCurrentAcceleration == 'function').toBe(true);
+        });
+
+        it("success callback should be called with an Acceleration object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(a.x).toBeDefined();
+                    expect(typeof a.x == 'number').toBe(true);
+                    expect(a.y).toBeDefined();
+                    expect(typeof a.y == 'number').toBe(true);
+                    expect(a.z).toBeDefined();
+                    expect(typeof a.z == 'number').toBe(true);
+                    expect(a.timestamp).toBeDefined();
+                    expect(typeof a.timestamp).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.accelerometer.getCurrentAcceleration(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("success callback Acceleration object should have (reasonable) values for x, y and z expressed in m/s^2", function() {
+            var reasonableThreshold = 15;
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a.x).toBeLessThan(reasonableThreshold);
+                    expect(a.x).toBeGreaterThan(reasonableThreshold * -1);
+                    expect(a.y).toBeLessThan(reasonableThreshold);
+                    expect(a.y).toBeGreaterThan(reasonableThreshold * -1);
+                    expect(a.z).toBeLessThan(reasonableThreshold);
+                    expect(a.z).toBeGreaterThan(reasonableThreshold * -1);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.accelerometer.getCurrentAcceleration(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("success callback Acceleration object should return a recent timestamp", function() {
+            var veryRecently = (new Date()).getTime();
+            // Need to check that dates returned are not vastly greater than a recent time stamp.
+            // In case the timestamps returned are ridiculously high
+            var reasonableTimeLimit = veryRecently + 5000; // 5 seconds from now
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a.timestamp).toBeGreaterThan(veryRecently);
+                    expect(a.timestamp).toBeLessThan(reasonableTimeLimit);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                navigator.accelerometer.getCurrentAcceleration(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe("watchAcceleration", function() {
+        var id;
+
+        afterEach(function() {
+            navigator.accelerometer.clearWatch(id);
+        });
+
+        it("should exist", function() {
+            expect(navigator.accelerometer.watchAcceleration).toBeDefined();
+            expect(typeof navigator.accelerometer.watchAcceleration == 'function').toBe(true);
+        });
+        it("success callback should be called with an Acceleration object", function() {
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a).toBeDefined();
+                    expect(a.x).toBeDefined();
+                    expect(typeof a.x == 'number').toBe(true);
+                    expect(a.y).toBeDefined();
+                    expect(typeof a.y == 'number').toBe(true);
+                    expect(a.z).toBeDefined();
+                    expect(typeof a.z == 'number').toBe(true);
+                    expect(a.timestamp).toBeDefined();
+                    expect(typeof a.timestamp).toBe('number');
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                id = navigator.accelerometer.watchAcceleration(win, fail, {frequency:500});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("success callback Acceleration object should have (reasonable) values for x, y and z expressed in m/s^2", function() {
+            var reasonableThreshold = 15;
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a.x).toBeLessThan(reasonableThreshold);
+                    expect(a.x).toBeGreaterThan(reasonableThreshold * -1);
+                    expect(a.y).toBeLessThan(reasonableThreshold);
+                    expect(a.y).toBeGreaterThan(reasonableThreshold * -1);
+                    expect(a.z).toBeLessThan(reasonableThreshold);
+                    expect(a.z).toBeGreaterThan(reasonableThreshold * -1);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                id = navigator.accelerometer.watchAcceleration(win, fail, {frequency:500});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("success callback Acceleration object should return a recent timestamp", function() {
+            var veryRecently = (new Date()).getTime();
+            // Need to check that dates returned are not vastly greater than a recent time stamp.
+            // In case the timestamps returned are ridiculously high
+            var reasonableTimeLimit = veryRecently + 5000; // 5 seconds from now
+            var win = jasmine.createSpy().andCallFake(function(a) {
+                    expect(a.timestamp).toBeGreaterThan(veryRecently);
+                    expect(a.timestamp).toBeLessThan(reasonableTimeLimit);
+                }),
+                fail = jasmine.createSpy();
+
+            runs(function () {
+                id = navigator.accelerometer.watchAcceleration(win, fail, {frequency:500});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe("clearWatch", function() {
+        it("should exist", function() {
+            expect(navigator.accelerometer.clearWatch).toBeDefined();
+            expect(typeof navigator.accelerometer.clearWatch == 'function').toBe(true);
+        });
+
+        it("should clear an existing watch", function() {
+            var id,
+                win = jasmine.createSpy();
+
+            runs(function() {
+                id = navigator.accelerometer.watchAcceleration(win, function() {}, {frequency:100});
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                win.reset();
+                navigator.accelerometer.clearWatch(id);
+            });
+
+            waits(201);
+
+            runs(function() {
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/battery.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/battery.tests.js
new file mode 100644
index 0000000..7bb25af
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/battery.tests.js
@@ -0,0 +1,5 @@
+describe('Battery (navigator.battery)', function () {;
+    it("should exist", function() {
+        expect(navigator.battery).toBeDefined();
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/camera.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/camera.tests.js
new file mode 100644
index 0000000..9b6b04c
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/camera.tests.js
@@ -0,0 +1,47 @@
+describe('Camera (navigator.camera)', function () {
+	it("should exist", function() {
+        expect(navigator.camera).toBeDefined();
+	});
+
+	it("should contain a getPicture function", function() {
+        expect(navigator.camera.getPicture).toBeDefined();
+		expect(typeof navigator.camera.getPicture == 'function').toBe(true);
+	});
+});
+
+describe('Camera Constants (window.Camera + navigator.camera)', function () {
+    it("window.Camera should exist", function() {
+        expect(window.Camera).toBeDefined();
+    });
+
+    it("should contain two DestinationType constants", function() {
+        expect(Camera.DestinationType.DATA_URL).toBe(0);
+        expect(Camera.DestinationType.FILE_URI).toBe(1);
+        expect(navigator.camera.DestinationType.DATA_URL).toBe(0);
+        expect(navigator.camera.DestinationType.FILE_URI).toBe(1);
+    });
+
+    it("should contain two EncodingType constants", function() {
+        expect(Camera.EncodingType.JPEG).toBe(0);
+        expect(Camera.EncodingType.PNG).toBe(1);
+        expect(navigator.camera.EncodingType.JPEG).toBe(0);
+        expect(navigator.camera.EncodingType.PNG).toBe(1);
+    });
+
+    it("should contain three MediaType constants", function() {
+        expect(Camera.MediaType.PICTURE).toBe(0);
+        expect(Camera.MediaType.VIDEO).toBe(1);
+        expect(Camera.MediaType.ALLMEDIA).toBe(2);
+        expect(navigator.camera.MediaType.PICTURE).toBe(0);
+        expect(navigator.camera.MediaType.VIDEO).toBe(1);
+        expect(navigator.camera.MediaType.ALLMEDIA).toBe(2);
+    });
+    it("should contain three PictureSourceType constants", function() {
+        expect(Camera.PictureSourceType.PHOTOLIBRARY).toBe(0);
+        expect(Camera.PictureSourceType.CAMERA).toBe(1);
+        expect(Camera.PictureSourceType.SAVEDPHOTOALBUM).toBe(2);
+        expect(navigator.camera.PictureSourceType.PHOTOLIBRARY).toBe(0);
+        expect(navigator.camera.PictureSourceType.CAMERA).toBe(1);
+        expect(navigator.camera.PictureSourceType.SAVEDPHOTOALBUM).toBe(2);
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/capture.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/capture.tests.js
new file mode 100644
index 0000000..bffced8
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/capture.tests.js
@@ -0,0 +1,95 @@
+describe('Capture (navigator.device.capture)', function () {
+    it("should exist", function() {
+        expect(navigator.device).toBeDefined();
+        expect(navigator.device.capture).toBeDefined();
+    });
+
+    it("should have the correct properties ", function() {
+        expect(navigator.device.capture.supportedAudioModes).toBeDefined();
+        expect(navigator.device.capture.supportedImageModes).toBeDefined();
+        expect(navigator.device.capture.supportedVideoModes).toBeDefined();
+    });
+
+    it("should contain a captureAudio function", function() {
+        expect(navigator.device.capture.captureAudio).toBeDefined();
+        expect(typeof navigator.device.capture.captureAudio == 'function').toBe(true);
+    });
+
+    it("should contain a captureImage function", function() {
+        expect(navigator.device.capture.captureImage).toBeDefined();
+        expect(typeof navigator.device.capture.captureImage == 'function').toBe(true);
+    });
+
+    it("should contain a captureVideo function", function() {
+        expect(navigator.device.capture.captureVideo).toBeDefined();
+        expect(typeof navigator.device.capture.captureVideo == 'function').toBe(true);
+    });
+
+    describe('CaptureAudioOptions', function () {
+        it("CaptureAudioOptions constructor should exist", function() {
+            var options = new CaptureAudioOptions();
+            expect(options).toBeDefined();
+            expect(options.limit).toBeDefined();
+            expect(options.duration).toBeDefined();
+            expect(options.mode).toBeDefined();
+        });
+    });
+
+    describe('CaptureImageOptions', function () {
+        it("CaptureImageOptions constructor should exist", function() {
+            var options = new CaptureImageOptions();
+            expect(options).toBeDefined();
+            expect(options.limit).toBeDefined();
+            expect(options.mode).toBeDefined();
+        });
+    });
+
+    describe('CaptureVideoOptions', function () {
+        it("CaptureVideoOptions constructor should exist", function() {
+            var options = new CaptureVideoOptions();
+            expect(options).toBeDefined();
+            expect(options.limit).toBeDefined();
+            expect(options.duration).toBeDefined();
+            expect(options.mode).toBeDefined();
+        });
+    });
+
+    describe('CaptureError interface', function () {
+        it("CaptureError constants should be defined", function() {
+            expect(CaptureError.CAPTURE_INTERNAL_ERR).toBe(0);
+            expect(CaptureError.CAPTURE_APPLICATION_BUSY).toBe(1);
+            expect(CaptureError.CAPTURE_INVALID_ARGUMENT).toBe(2);
+            expect(CaptureError.CAPTURE_NO_MEDIA_FILES).toBe(3);
+        });
+
+        it("CaptureError properties should exist", function() {
+            var error = new CaptureError();
+            expect(error).toBeDefined();
+            expect(error.code).toBeDefined();
+        });
+    });
+
+    describe('MediaFileData', function () {
+        it("MediaFileData constructor should exist", function() {
+            var fileData = new MediaFileData();
+            expect(fileData).toBeDefined();
+            expect(fileData.bitrate).toBeDefined();
+            expect(fileData.codecs).toBeDefined();
+            expect(fileData.duration).toBeDefined();
+            expect(fileData.height).toBeDefined();
+            expect(fileData.width).toBeDefined();
+        });
+    });
+
+    describe('MediaFile', function () {
+        it("MediaFile constructor should exist", function() {
+            var fileData = new MediaFile();
+            expect(fileData).toBeDefined();
+            expect(fileData.name).toBeDefined();
+            expect(fileData.fullPath).toBeDefined();
+            expect(fileData.type).toBeDefined();
+            expect(fileData.lastModifiedDate).toBeDefined();
+            expect(fileData.size).toBeDefined();
+        });
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/compass.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/compass.tests.js
new file mode 100644
index 0000000..a16ec0e
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/compass.tests.js
@@ -0,0 +1,76 @@
+describe('Compass (navigator.compass)', function () {
+    it("should exist", function() {
+        expect(navigator.compass).toBeDefined();
+    });
+
+    it("should contain a getCurrentHeading function", function() {
+        expect(navigator.compass.getCurrentHeading).toBeDefined();
+		expect(typeof navigator.compass.getCurrentHeading == 'function').toBe(true);
+	});
+
+    it("getCurrentHeading success callback should be called with a Heading object", function() {
+        var win = jasmine.createSpy().andCallFake(function(a) {
+                expect(a instanceof CompassHeading).toBe(true);
+                expect(a.magneticHeading).toBeDefined();
+                expect(typeof a.magneticHeading == 'number').toBe(true);
+                expect(a.trueHeading).not.toBe(undefined);
+                expect(typeof a.trueHeading == 'number' || a.trueHeading === null).toBe(true);
+                expect(a.headingAccuracy).not.toBe(undefined);
+                expect(typeof a.headingAccuracy == 'number' || a.headingAccuracy === null).toBe(true);
+                expect(typeof a.timestamp == 'number').toBe(true);
+            }),
+            fail = jasmine.createSpy();
+
+        runs(function () {
+            navigator.compass.getCurrentHeading(win, fail);
+        });
+
+        waitsFor(function () { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+        runs(function () {
+            expect(fail).not.toHaveBeenCalled();
+            expect(win).toHaveBeenCalled();
+        });
+	});
+
+    it("should contain a watchHeading function", function() {
+        expect(navigator.compass.watchHeading).toBeDefined();
+        expect(typeof navigator.compass.watchHeading == 'function').toBe(true);
+    });
+
+    it("should contain a clearWatch function", function() {
+        expect(navigator.compass.clearWatch).toBeDefined();
+        expect(typeof navigator.compass.clearWatch == 'function').toBe(true);
+    });
+
+    describe('Compass Constants (window.CompassError)', function () {
+        it("should exist", function() {
+            expect(window.CompassError).toBeDefined();
+            expect(window.CompassError.COMPASS_INTERNAL_ERR).toBe(0);
+            expect(window.CompassError.COMPASS_NOT_SUPPORTED).toBe(20);
+        });
+    });
+
+    describe('Compass Heading model (CompassHeading)', function () {
+        it("should exist", function() {
+            expect(CompassHeading).toBeDefined();
+        });
+
+        it("should be able to create a new CompassHeading instance with no parameters", function() {
+            var h = new CompassHeading();
+            expect(h.magneticHeading).toBeDefined();
+            expect(h.trueHeading).toBeDefined();
+            expect(h.headingAccuracy).toBeDefined();
+            expect(typeof h.timestamp == 'number').toBe(true);
+        });
+
+        it("should be able to creat a new CompassHeading instance with parameters", function() {
+            var h = new CompassHeading(1,2,3,4);
+            expect(h.magneticHeading).toBe(1);
+            expect(h.trueHeading).toBe(2);
+            expect(h.headingAccuracy).toBe(3);
+            expect(h.timestamp.valueOf()).toBe(4);
+            expect(typeof h.timestamp == 'number').toBe(true);
+        });
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/contacts.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/contacts.tests.js
new file mode 100644
index 0000000..a5f41a5
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/contacts.tests.js
@@ -0,0 +1,451 @@
+// global to store a contact so it doesn't have to be created or retrieved multiple times
+// all of the setup/teardown test methods can reference the following variables to make sure to do the right cleanup
+var gContactObj = null;
+var gContactId = null;
+
+var removeContact = function(){
+    if (gContactObj) {
+        gContactObj.remove(function(){},function(){
+            console.log("[CONTACTS ERROR]: removeContact cleanup method failed to clean up test artifacts.");
+        });
+        gContactObj = null;
+    }
+};
+
+describe("Contacts (navigator.contacts)", function () {
+    it("should exist", function() {
+        expect(navigator.contacts).toBeDefined();
+    });
+
+    it("should contain a find function", function() {
+        expect(navigator.contacts.find).toBeDefined();
+        expect(typeof navigator.contacts.find).toBe('function');
+    });
+
+    describe("find method", function() {
+        it("success callback should be called with an array", function() {
+            var win = jasmine.createSpy().andCallFake(function(result) {
+                    expect(result).toBeDefined();
+                    expect(result instanceof Array).toBe(true);
+                }),
+                fail = jasmine.createSpy(),
+                obj = new ContactFindOptions();
+
+            runs(function () {
+                obj.filter="";
+                obj.multiple=true;
+                navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], win, fail, obj);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("should throw an exception if success callback is empty", function() {
+            var fail = function() {};
+            var obj = new ContactFindOptions();
+            obj.filter="";
+            obj.multiple=true;
+
+            expect(function () {
+                navigator.contacts.find(["displayName", "name", "emails", "phoneNumbers"], null, fail, obj);
+            }).toThrow();
+        });
+
+        it("error callback should be called when no fields are specified", function() {
+            var win = jasmine.createSpy(),
+                fail = jasmine.createSpy(function(result) {
+                    expect(result).toBeDefined();
+                    expect(result.code).toBe(ContactError.INVALID_ARGUMENT_ERROR);
+                }),
+                obj = new ContactFindOptions();
+
+            runs(function () {
+                obj.filter="";
+                obj.multiple=true;
+                navigator.contacts.find([], win, fail, obj);
+            });
+
+            waitsFor(function () { return fail.wasCalled; }, Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(win).not.toHaveBeenCalled();
+                expect(fail).toHaveBeenCalled();
+            });
+        });
+
+        describe("with newly-created contact", function () {
+
+            afterEach(removeContact);
+
+            it("should be able to find a contact by name", function() {
+                var foundName = jasmine.createSpy().andCallFake(function(result) {
+                        var bFound = false;
+                        try {
+                            for (var i=0; i < result.length; i++) {
+                                if (result[i].name.familyName == "Delete") {
+                                    bFound = true;
+                                    break;
+                                }
+                            }
+                        } catch(e) {
+                            return false;
+                        }
+                        return bFound;
+                    }),
+                    fail = jasmine.createSpy(),
+                    test = jasmine.createSpy().andCallFake(function(savedContact) {
+                        console.log('in test');
+                        // update so contact will get removed
+                        gContactObj = savedContact;
+                        // ----
+                        // Find asserts
+                        // ---
+                        var findWin = jasmine.createSpy().andCallFake(function(object) {
+                                console.log('in findwin');
+                                expect(object instanceof Array).toBe(true);
+                                expect(object.length >= 1).toBe(true);
+                                expect(foundName(object)).toBe(true);
+                            }),
+                            findFail = jasmine.createSpy(),
+                            obj = new ContactFindOptions();
+
+                        obj.filter="Delete";
+                        obj.multiple=true;
+
+                        runs(function () {
+                            navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWin, findFail, obj);
+                        });
+
+                        waitsFor(function () { return foundName.wasCalled; }, "foundName not done", Tests.TEST_TIMEOUT);
+
+                        runs(function () {
+                            expect(findFail).not.toHaveBeenCalled();
+                            expect(fail).not.toHaveBeenCalled();
+                        });
+                    });
+
+                runs(function () {
+                    gContactObj = new Contact();
+                    gContactObj.name = new ContactName();
+                    gContactObj.name.familyName = "Delete";
+                    gContactObj.save(test, fail);
+                });
+
+                waitsFor(function () { return test.wasCalled; }, "test not done", Tests.TEST_TIMEOUT);
+            });
+        });
+    });
+
+    describe('create method', function() {
+
+        it("should exist", function() {
+            expect(navigator.contacts.create).toBeDefined();
+            expect(typeof navigator.contacts.create).toBe('function');
+        });
+
+        it("should return a Contact object", function() {
+            var bDay = new Date(1976, 7,4);
+            var obj = navigator.contacts.create({"displayName": "test name", "gender": "male", "note": "my note", "name": {"formatted": "Mr. Test Name"}, "emails": [{"value": "here@there.com"}, {"value": "there@here.com"}], "birthday": bDay});
+
+            expect(obj).toBeDefined();
+            expect(obj.displayName).toBe('test name');
+            expect(obj.note).toBe('my note');
+            expect(obj.name.formatted).toBe('Mr. Test Name');
+            expect(obj.emails.length).toBe(2);
+            expect(obj.emails[0].value).toBe('here@there.com');
+            expect(obj.emails[1].value).toBe('there@here.com');
+            expect(obj.nickname).toBe(null);
+            expect(obj.birthday).toBe(bDay);
+        });
+    });
+
+    describe("Contact object", function () {
+        it("should be able to create instance", function() {
+            var contact = new Contact("a", "b", new ContactName("a", "b", "c", "d", "e", "f"), "c", [], [], [], [], [], "f", "i",
+                [], [], []);
+            expect(contact).toBeDefined();
+            expect(contact.id).toBe("a");
+            expect(contact.displayName).toBe("b");
+            expect(contact.name.formatted).toBe("a");
+            expect(contact.nickname).toBe("c");
+            expect(contact.phoneNumbers).toBeDefined();
+            expect(contact.emails).toBeDefined();
+            expect(contact.addresses).toBeDefined();
+            expect(contact.ims).toBeDefined();
+            expect(contact.organizations).toBeDefined();
+            expect(contact.birthday).toBe("f");
+            expect(contact.note).toBe("i");
+            expect(contact.photos).toBeDefined();
+            expect(contact.categories).toBeDefined();
+            expect(contact.urls).toBeDefined();
+        });
+
+        it("should be able to define a ContactName object", function() {
+            var contactName = new ContactName("Dr. First Last Jr.", "Last", "First", "Middle", "Dr.", "Jr.");
+            expect(contactName).toBeDefined();
+            expect(contactName.formatted).toBe("Dr. First Last Jr.");
+            expect(contactName.familyName).toBe("Last");
+            expect(contactName.givenName).toBe("First");
+            expect(contactName.middleName).toBe("Middle");
+            expect(contactName.honorificPrefix).toBe("Dr.");
+            expect(contactName.honorificSuffix).toBe("Jr.");
+        });
+
+        it("should be able to define a ContactField object", function() {
+            var contactField = new ContactField("home", "8005551212", true);
+            expect(contactField).toBeDefined();
+            expect(contactField.type).toBe("home");
+            expect(contactField.value).toBe("8005551212");
+            expect(contactField.pref).toBe(true);
+        });
+
+        it("ContactField object should coerce type and value properties to strings", function() {
+            var contactField = new ContactField(12345678, 12345678, true);
+            expect(contactField.type).toBe("12345678");
+            expect(contactField.value).toBe("12345678");
+        });
+
+        it("should be able to define a ContactAddress object", function() {
+            var contactAddress = new ContactAddress(true, "home", "a","b","c","d","e","f");
+            expect(contactAddress).toBeDefined();
+            expect(contactAddress.pref).toBe(true);
+            expect(contactAddress.type).toBe("home");
+            expect(contactAddress.formatted).toBe("a");
+            expect(contactAddress.streetAddress).toBe("b");
+            expect(contactAddress.locality).toBe("c");
+            expect(contactAddress.region).toBe("d");
+            expect(contactAddress.postalCode).toBe("e");
+            expect(contactAddress.country).toBe("f");
+        });
+
+        it("should be able to define a ContactOrganization object", function() {
+            var contactOrg = new ContactOrganization(true, "home", "a","b","c","d","e","f","g");
+            expect(contactOrg).toBeDefined();
+            expect(contactOrg.pref).toBe(true);
+            expect(contactOrg.type).toBe("home");
+            expect(contactOrg.name).toBe("a");
+            expect(contactOrg.department).toBe("b");
+            expect(contactOrg.title).toBe("c");
+        });
+
+        it("should be able to define a ContactFindOptions object", function() {
+            var contactFindOptions = new ContactFindOptions("a", true, "b");
+            expect(contactFindOptions).toBeDefined();
+            expect(contactFindOptions.filter).toBe("a");
+            expect(contactFindOptions.multiple).toBe(true);
+        });
+
+        it("should contain a clone function", function() {
+            var contact = new Contact();
+            expect(contact.clone).toBeDefined();
+            expect(typeof contact.clone).toBe('function');
+        });
+
+        it("clone function should make deep copy of Contact Object", function() {
+            var contact = new Contact();
+            contact.id=1;
+            contact.displayName="Test Name";
+            contact.nickname="Testy";
+            contact.gender="male";
+            contact.note="note to be cloned";
+            contact.name = new ContactName("Mr. Test Name");
+
+            var clonedContact = contact.clone();
+
+            expect(contact.id).toBe(1);
+            expect(clonedContact.id).toBe(null);
+            expect(clonedContact.displayName).toBe(contact.displayName);
+            expect(clonedContact.nickname).toBe(contact.nickname);
+            expect(clonedContact.gender).toBe(contact.gender);
+            expect(clonedContact.note).toBe(contact.note);
+            expect(clonedContact.name.formatted).toBe(contact.name.formatted);
+            expect(clonedContact.connected).toBe(contact.connected);
+        });
+
+        it("should contain a save function", function() {
+            var contact = new Contact();
+            expect(contact.save).toBeDefined();
+            expect(typeof contact.save).toBe('function');
+        });
+
+        it("should contain a remove function", function() {
+            var contact = new Contact();
+            expect(contact.remove).toBeDefined();
+            expect(typeof contact.remove).toBe('function');
+        });
+    });
+
+    describe('save method', function () {
+        it("should be able to save a contact", function() {
+            var bDay = new Date(1976, 6,4);
+            gContactObj = navigator.contacts.create({"gender": "male", "note": "my note", "name": {"familyName": "Delete", "givenName": "Test"}, "emails": [{"value": "here@there.com"}, {"value": "there@here.com"}], "birthday": bDay});
+
+            var saveSuccess = jasmine.createSpy().andCallFake(function(obj) {
+                    expect(obj).toBeDefined();
+                    expect(obj.note).toBe('my note');
+                    expect(obj.name.familyName).toBe('Delete');
+                    expect(obj.name.givenName).toBe('Test');
+                    expect(obj.emails.length).toBe(2);
+                    expect(obj.emails[0].value).toBe('here@there.com');
+                    expect(obj.emails[1].value).toBe('there@here.com');
+                    expect(obj.birthday.toDateString()).toBe(bDay.toDateString());
+                    expect(obj.addresses).toBe(null);
+                    // must store returned object in order to have id for update test below
+                    gContactObj = obj;
+                }),
+                saveFail = jasmine.createSpy();
+
+            runs(function () {
+                gContactObj.save(saveSuccess, saveFail);
+            });
+
+            waitsFor(function () { return saveSuccess.wasCalled; }, "saveSuccess never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(saveFail).not.toHaveBeenCalled();
+            });
+         });
+        // HACK: there is a reliance between the previous and next test. This is bad form.
+        it("update a contact", function() {
+            expect(gContactObj).toBeDefined();
+
+            var bDay = new Date(1975, 5,4);
+            var noteText = "an UPDATED note";
+
+            var win = jasmine.createSpy().andCallFake(function(obj) {
+                    expect(obj).toBeDefined();
+                    expect(obj.id).toBe(gContactObj.id);
+                    expect(obj.note).toBe(noteText);
+                    expect(obj.birthday.toDateString()).toBe(bDay.toDateString());
+                    expect(obj.emails.length).toBe(1);
+                    expect(obj.emails[0].value).toBe('here@there.com');
+                    removeContact();         // Clean up contact object
+                }), fail = jasmine.createSpy().andCallFake(removeContact);
+
+            runs(function () {
+                // remove an email
+                gContactObj.emails[1].value = "";
+                // change birthday
+                gContactObj.birthday = bDay;
+                // update note
+                gContactObj.note = noteText;
+                gContactObj.save(win, fail);
+            });
+
+            waitsFor(function () { return win.wasCalled; }, "saveSuccess never called", Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe('Contact.remove method', function () {
+        afterEach(removeContact);
+
+        it("calling remove on a contact has an id of null should return ContactError.UNKNOWN_ERROR", function() {
+            var win = jasmine.createSpy();
+            var fail = jasmine.createSpy().andCallFake(function(result) {
+                expect(result.code).toBe(ContactError.UNKNOWN_ERROR);
+            });
+
+            runs(function () {
+                var rmContact = new Contact();
+                rmContact.remove(win, fail);
+            });
+
+            waitsFor(function () { return fail.wasCalled; }, Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+
+        it("calling remove on a contact that does not exist should return ContactError.UNKNOWN_ERROR", function() {
+            var win = jasmine.createSpy();
+            var fail = jasmine.createSpy().andCallFake(function(result) {
+                expect(result.code).toBe(ContactError.UNKNOWN_ERROR);
+            });
+
+            runs(function () {
+                var rmContact = new Contact();
+                // this is a bit risky as some devices may have contact ids that large
+                var contact = new Contact("this string is supposed to be a unique identifier that will never show up on a device");
+                contact.remove(win, fail);
+            });
+
+            waitsFor(function () { return fail.wasCalled; }, Tests.TEST_TIMEOUT);
+
+            runs(function () {
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe("Round trip Contact tests (creating + save + delete + find).", function () {
+        afterEach(removeContact);
+
+        it("Creating, saving, finding a contact should work, removing it should work, after which we should not be able to find it, and we should not be able to delete it again.", function() {
+            var done = false;
+            runs(function () {
+                gContactObj = new Contact();
+                gContactObj.name = new ContactName();
+                gContactObj.name.familyName = "DeleteMe";
+                gContactObj.save(function(c_obj) {
+                    var findWin = function(cs) {
+                        expect(cs.length).toBe(1);
+                        // update to have proper saved id
+                        gContactObj = cs[0];
+                        gContactObj.remove(function() {
+                            var findWinAgain = function(seas) {
+                                expect(seas.length).toBe(0);
+                                gContactObj.remove(function() {
+                                    throw("success callback called after non-existent Contact object called remove(). Test failed.");
+                                }, function(e) {
+                                    expect(e.code).toBe(ContactError.UNKNOWN_ERROR);
+                                    done = true;
+                                });
+                            };
+                            var findFailAgain = function(e) {
+                                throw("find error callback invoked after delete, test failed.");
+                            };
+                            var obj = new ContactFindOptions();
+                            obj.filter="DeleteMe";
+                            obj.multiple=true;
+                            navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWinAgain, findFailAgain, obj);
+                        }, function(e) {
+                            throw("Newly created contact's remove function invoked error callback. Test failed.");
+                        });
+                    };
+                    var findFail = function(e) {
+                        throw("Failure callback invoked in navigator.contacts.find call, test failed.");
+                    };
+                    var obj = new ContactFindOptions();
+                    obj.filter="DeleteMe";
+                    obj.multiple=true;
+                    navigator.contacts.find(["displayName", "name", "phoneNumbers", "emails"], findWin, findFail, obj);
+                }, function(e) {
+                    throw("Contact creation failed, error callback was invoked.");
+                });
+            });
+
+            waitsFor(function () { return done; }, Tests.TEST_TIMEOUT);
+        });
+    });
+
+    describe('ContactError interface', function () {
+        it("ContactError constants should be defined", function() {
+            expect(ContactError.UNKNOWN_ERROR).toBe(0);
+            expect(ContactError.INVALID_ARGUMENT_ERROR).toBe(1);
+            expect(ContactError.TIMEOUT_ERROR).toBe(2);
+            expect(ContactError.PENDING_OPERATION_ERROR).toBe(3);
+            expect(ContactError.IO_ERROR).toBe(4);
+            expect(ContactError.NOT_SUPPORTED_ERROR).toBe(5);
+            expect(ContactError.PERMISSION_DENIED_ERROR).toBe(20);
+        });
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/device.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/device.tests.js
new file mode 100644
index 0000000..cc322d6
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/device.tests.js
@@ -0,0 +1,34 @@
+describe('Device Information (window.device)', function () {
+	it("should exist", function() {
+        expect(window.device).toBeDefined();
+	});
+
+	it("should contain a platform specification that is a string", function() {
+        expect(window.device.platform).toBeDefined();
+		expect((new String(window.device.platform)).length > 0).toBe(true);
+	});
+
+	it("should contain a version specification that is a string", function() {
+        expect(window.device.version).toBeDefined();
+		expect((new String(window.device.version)).length > 0).toBe(true);
+	});
+
+	it("should contain a name specification that is a string", function() {
+        expect(window.device.name).toBeDefined();
+		expect((new String(window.device.name)).length > 0).toBe(true);
+	});
+
+	it("should contain a UUID specification that is a string or a number", function() {
+        expect(window.device.uuid).toBeDefined();
+		if (typeof window.device.uuid == 'string' || typeof window.device.uuid == 'object') {
+		    expect((new String(window.device.uuid)).length > 0).toBe(true);
+		} else {
+			expect(window.device.uuid > 0).toBe(true);
+		}
+	});
+
+	it("should contain a cordova specification that is a string", function() {
+        expect(window.device.cordova).toBeDefined();
+		expect((new String(window.device.cordova)).length > 0).toBe(true);
+	});
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/file.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/file.tests.js
new file mode 100644
index 0000000..d1e7db4
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/file.tests.js
@@ -0,0 +1,3462 @@
+describe('File API', function() {
+    // Adding a Jasmine helper matcher, to report errors when comparing to FileError better.
+    var fileErrorMap = {
+        1: 'NOT_FOUND_ERR',
+        2: 'SECURITY_ERR',
+        3: 'ABORT_ERR',
+        4: 'NOT_READABLE_ERR',
+        5: 'ENCODING_ERR',
+        6: 'NO_MODIFICATION_ALLOWED_ERR',
+        7: 'INVALID_STATE_ERR',
+        8: 'SYNTAX_ERR',
+        9: 'INVALID_MODIFICATION_ERR',
+        10:'QUOTA_EXCEEDED_ERR',
+        11:'TYPE_MISMATCH_ERR',
+        12:'PATH_EXISTS_ERR'
+    };
+    beforeEach(function() {
+        this.addMatchers({
+            toBeFileError: function(code) {
+                var error = this.actual;
+                this.message = function(){
+                    return "Expected FileError with code " + fileErrorMap[error.code] + " (" + error.code + ") to be " + fileErrorMap[code] + "(" + code + ")";
+                };
+                return (error.code == code);
+            },
+            toCanonicallyMatch:function(path){
+                this.message = function(){
+                    return "Expected paths to match : " + path + " should be " + this.actual;
+                };
+
+                var a = path.split("/").join("").split("\\").join("");
+                var b = this.actual.split("/").join("").split("\\").join("");
+
+                return a == b;
+            }
+        });
+    });
+
+    // HELPER FUNCTIONS
+
+    // deletes specified file or directory
+    var deleteEntry = function(name, success, error) {
+        // deletes entry, if it exists
+        window.resolveLocalFileSystemURI(root.toURL() + '/' + name,
+            function(entry) {
+                if (entry.isDirectory === true) {
+                    entry.removeRecursively(success, error);
+                } else {
+                    entry.remove(success, error);
+                }
+            }, success);
+    };
+    // deletes file, if it exists, then invokes callback
+    var deleteFile = function(fileName, callback) {
+        root.getFile(fileName, null,
+                // remove file system entry
+                function(entry) {
+                    entry.remove(callback, function() { console.log('[ERROR] deleteFile cleanup method invoked fail callback.'); });
+                },
+                // doesn't exist
+                callback);
+    };
+    // deletes and re-creates the specified file
+    var createFile = function(fileName, success, error) {
+        deleteEntry(fileName, function() {
+            root.getFile(fileName, {create: true}, success, error);
+        }, error);
+    };
+    // deletes and re-creates the specified directory
+    var createDirectory = function(dirName, success, error) {
+        deleteEntry(dirName, function() {
+           root.getDirectory(dirName, {create: true}, success, error);
+        }, error);
+    };
+
+    var createFail = function(module) {
+        return jasmine.createSpy().andCallFake(function(err) {
+            console.log('[ERROR ' + module + '] ' + JSON.stringify(err));
+        });
+    };
+
+    var createWin = function(module) {
+        return jasmine.createSpy().andCallFake(function() {
+            console.log('[ERROR ' + module + '] Unexpected success callback');
+        });
+    };
+
+    describe('FileError object', function() {
+        it("should define FileError constants", function() {
+            expect(FileError.NOT_FOUND_ERR).toBe(1);
+            expect(FileError.SECURITY_ERR).toBe(2);
+            expect(FileError.ABORT_ERR).toBe(3);
+            expect(FileError.NOT_READABLE_ERR).toBe(4);
+            expect(FileError.ENCODING_ERR).toBe(5);
+            expect(FileError.NO_MODIFICATION_ALLOWED_ERR).toBe(6);
+            expect(FileError.INVALID_STATE_ERR).toBe(7);
+            expect(FileError.SYNTAX_ERR).toBe(8);
+            expect(FileError.INVALID_MODIFICATION_ERR).toBe(9);
+            expect(FileError.QUOTA_EXCEEDED_ERR).toBe(10);
+            expect(FileError.TYPE_MISMATCH_ERR).toBe(11);
+            expect(FileError.PATH_EXISTS_ERR).toBe(12);
+        });
+    });
+
+    describe('LocalFileSystem', function() {
+
+        it("should define LocalFileSystem constants", function() {
+            expect(LocalFileSystem.TEMPORARY).toBe(0);
+            expect(LocalFileSystem.PERSISTENT).toBe(1);
+        });
+
+        describe('window.requestFileSystem', function() {
+            it("should be defined", function() {
+                expect(window.requestFileSystem).toBeDefined();
+            });
+            it("should be able to retrieve a PERSISTENT file system", function() {
+                var win = jasmine.createSpy().andCallFake(function(fileSystem) {
+                    expect(fileSystem).toBeDefined();
+                    expect(fileSystem.name).toBeDefined();
+                    expect(fileSystem.name).toBe("persistent");
+                    expect(fileSystem.root).toBeDefined();
+                }),
+                fail = createFail('window.requestFileSystem');
+
+                // retrieve PERSISTENT file system
+                runs(function() {
+                    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, win, fail);
+                });
+
+                waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(fail).not.toHaveBeenCalled();
+                    expect(win).toHaveBeenCalled();
+                });
+            });
+            it("should be able to retrieve a TEMPORARY file system", function() {
+                var win = jasmine.createSpy().andCallFake(function(fileSystem) {
+                    expect(fileSystem).toBeDefined();
+                    expect(fileSystem.name).toBeDefined();
+                    expect(fileSystem.name).toBe("temporary");
+                    expect(fileSystem.root).toBeDefined();
+                }),
+                fail = createFail('window.requestFileSystem');
+
+                // Request the file system
+                runs(function() {
+                    window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, win, fail);
+                });
+
+                waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(fail).not.toHaveBeenCalled();
+                    expect(win).toHaveBeenCalled();
+                });
+            });
+            it("should error if you request a file system that is too large", function() {
+                var fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.QUOTA_EXCEEDED_ERR);
+                }),
+                win = createWin('window.requestFileSystem');
+
+                // Request the file system
+                runs(function() {
+                    window.requestFileSystem(LocalFileSystem.TEMPORARY, 1000000000000000, win, fail);
+                });
+
+                waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(win).not.toHaveBeenCalled();
+                    expect(fail).toHaveBeenCalled();
+                });
+            });
+            it("should error out if you request a file system that does not exist", function() {
+                var fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.SYNTAX_ERR);
+                }),
+                win = createWin('window.requestFileSystem');
+
+                // Request the file system
+                runs(function() {
+                    window.requestFileSystem(-1, 0, win, fail);
+                });
+
+                waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(win).not.toHaveBeenCalled();
+                    expect(fail).toHaveBeenCalled();
+                });
+            });
+        });
+
+        describe('window.resolveLocalFileSystemURI', function() {
+            it("should be defined", function() {
+                expect(window.resolveLocalFileSystemURI).toBeDefined();
+            });
+            it("should resolve a valid file name", function() {
+                var fileName = "resolve.file.uri",
+                win = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.name).toCanonicallyMatch(fileName);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                }),
+                fail = createFail('window.resolveLocalFileSystemURI');
+                resolveCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // lookup file system entry
+                    runs(function() {
+                        window.resolveLocalFileSystemURI(entry.toURL(), win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "resolveLocalFileSystemURI callback never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                });
+
+                // create a new file entry
+                runs(function() {
+                    createFile(fileName, resolveCallback, fail);
+                });
+
+                waitsFor(function() { return resolveCallback.wasCalled; }, "createFile callback never called", Tests.TEST_TIMEOUT);
+            });
+            it("resolve valid file name with parameters", function() {
+                var fileName = "resolve.file.uri.params",
+                win = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.name).toBe(fileName);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                }),
+                fail = createFail('window.resolveLocalFileSystemURI');
+                resolveCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // lookup file system entry
+                    runs(function() {
+                        window.resolveLocalFileSystemURI(entry.toURL() + "?1234567890", win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "resolveLocalFileSystemURI callback never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                });
+
+                // create a new file entry
+                runs(function() {
+                    createFile(fileName, resolveCallback, fail);
+                });
+
+                waitsFor(function() { return resolveCallback.wasCalled; }, "createFile callback never called", Tests.TEST_TIMEOUT);
+            });
+            it("should error (NOT_FOUND_ERR) when resolving (non-existent) invalid file name", function() {
+                var fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                win = createWin('window.resolveLocalFileSystemURI');
+
+                // lookup file system entry
+                runs(function() {
+                    window.resolveLocalFileSystemURI("file:///this.is.not.a.valid.file.txt", win, fail);
+                });
+
+                waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(fail).toHaveBeenCalled();
+                    expect(win).not.toHaveBeenCalled();
+                });
+            });
+            it("should error (ENCODING_ERR) when resolving invalid URI with leading /", function() {
+                var fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.ENCODING_ERR);
+                }),
+                win = createWin('window.resolveLocalFileSystemURI');
+
+                // lookup file system entry
+                runs(function() {
+                    window.resolveLocalFileSystemURI("/this.is.not.a.valid.url", win, fail);
+                });
+
+                waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(fail).toHaveBeenCalled();
+                    expect(win).not.toHaveBeenCalled();
+                });
+            });
+        });
+    });
+
+    describe('Metadata interface', function() {
+        it("should exist and have the right properties", function() {
+            var metadata = new Metadata();
+            expect(metadata).toBeDefined();
+            expect(metadata.modificationTime).toBeDefined();
+        });
+    });
+
+    describe('Flags interface', function() {
+        it("should exist and have the right properties", function() {
+            var flags = new Flags(false, true);
+            expect(flags).toBeDefined();
+            expect(flags.create).toBeDefined();
+            expect(flags.create).toBe(false);
+            expect(flags.exclusive).toBeDefined();
+            expect(flags.exclusive).toBe(true);
+        });
+    });
+
+    describe('FileSystem interface', function() {
+        it("should have a root that is a DirectoryEntry", function() {
+            var win = jasmine.createSpy().andCallFake(function(entry) {
+                expect(entry).toBeDefined();
+                expect(entry.isFile).toBe(false);
+                expect(entry.isDirectory).toBe(true);
+                expect(entry.name).toBeDefined();
+                expect(entry.fullPath).toBeDefined();
+                expect(entry.getMetadata).toBeDefined();
+                expect(entry.moveTo).toBeDefined();
+                expect(entry.copyTo).toBeDefined();
+                expect(entry.toURL).toBeDefined();
+                expect(entry.remove).toBeDefined();
+                expect(entry.getParent).toBeDefined();
+                expect(entry.createReader).toBeDefined();
+                expect(entry.getFile).toBeDefined();
+                expect(entry.getDirectory).toBeDefined();
+                expect(entry.removeRecursively).toBeDefined();
+            }),
+            fail = createFail('FileSystem');
+
+            runs(function() {
+                window.resolveLocalFileSystemURI(root.toURL(), win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).not.toHaveBeenCalled();
+                expect(win).toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe('DirectoryEntry', function() {
+        it("getFile: get Entry for file that does not exist", function() {
+            var fileName = "de.no.file",
+                filePath = root.fullPath + '/' + fileName,
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create:false, exclusive:false, file does not exist
+            runs(function() {
+                root.getFile(fileName, {create:false}, win, fail);
+            });
+
+            waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+        it("etFile: create new file", function() {
+            var fileName = "de.create.file",
+                filePath = root.fullPath + '/' + fileName,
+                win = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toCanonicallyMatch(fileName);
+                    expect(entry.fullPath).toBe(filePath);
+                    // cleanup
+                    entry.remove(null, null);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:false, file does not exist
+            runs(function() {
+                root.getFile(fileName, {create: true}, win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("getFile: create new file (exclusive)", function() {
+            var fileName = "de.create.exclusive.file",
+                filePath = root.fullPath + '/' + fileName,
+                win = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toBe(fileName);
+                    expect(entry.fullPath).toBe(filePath);
+
+                    // cleanup
+                    entry.remove(null, null);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:true, file does not exist
+            runs(function() {
+                root.getFile(fileName, {create: true, exclusive:true}, win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("getFile: create file that already exists", function() {
+            var fileName = "de.create.existing.file",
+                filePath = root.fullPath + '/' + fileName,
+                getFile = jasmine.createSpy().andCallFake(function(file) {
+                    // create:true, exclusive:false, file exists
+                    runs(function() {
+                        root.getFile(fileName, {create:true}, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win was never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = createFail('DirectoryEntry'),
+                win = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toCanonicallyMatch(fileName);
+                    expect(entry.fullPath).toBe(filePath);
+
+                    // cleanup
+                    entry.remove(null, fail);
+                });
+            // create file to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, getFile, fail);
+            });
+
+            waitsFor(function() { return getFile.wasCalled; }, "getFile was never called", Tests.TEST_TIMEOUT);
+        });
+        it("getFile: create file that already exists (exclusive)", function() {
+            var fileName = "de.create.exclusive.existing.file",
+                filePath = root.fullPath + '/' + fileName,
+                existingFile,
+                getFile = jasmine.createSpy().andCallFake(function(file) {
+                    existingFile = file;
+                    // create:true, exclusive:true, file exists
+                    runs(function() {
+                        root.getFile(fileName, {create:true, exclusive:true}, win, fail);
+                    });
+
+                    waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.PATH_EXISTS_ERR);
+
+                    // cleanup
+                    existingFile.remove(null, fail);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create file to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, getFile, fail);
+            });
+
+            waitsFor(function() { return getFile.wasCalled; }, "getFile never called", Tests.TEST_TIMEOUT);
+        });
+        it("getFile: get Entry for existing file", function() {
+            var fileName = "de.get.file",
+                filePath = root.fullPath + '/' + fileName,
+                win = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toCanonicallyMatch(fileName);
+                    expect(entry.fullPath).toCanonicallyMatch(filePath);
+
+                    entry.remove(null, fail); //clean up
+                }),
+                fail = createFail('DirectoryEntry'),
+                getFile = jasmine.createSpy().andCallFake(function(file) {
+                    // create:false, exclusive:false, file exists
+                    runs(function() {
+                        root.getFile(fileName, {create:false}, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "getFile success callback", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                });
+
+            // create file to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, getFile, fail);
+            });
+
+            waitsFor(function() { return getFile.wasCalled; }, "file creation", Tests.TEST_TIMEOUT);
+        });
+        it("DirectoryEntry.getFile: get FileEntry for invalid path", function() {
+            var fileName = "de:invalid:path",
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.ENCODING_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create:false, exclusive:false, invalid path
+            runs(function() {
+                root.getFile(fileName, {create:false}, win, fail);
+            });
+
+            waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+            });
+
+        });
+        it("DirectoryEntry.getDirectory: get Entry for directory that does not exist", function() {
+            var dirName = "de.no.dir",
+                dirPath = root.fullPath + '/' + dirName,
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create:false, exclusive:false, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create:false}, win, fail);
+            });
+
+            waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+        it("DirectoryEntry.getDirectory: create new dir with space then resolveFileSystemURI", function() {
+            var dirName = "de create dir",
+                dirPath = root.fullPath + '/' + dirName,
+                getDir = jasmine.createSpy().andCallFake(function(dirEntry) {
+                    var dirURI = dirEntry.toURL();
+                    // now encode URI and try to resolve
+                    runs(function() {
+                        window.resolveLocalFileSystemURI(dirURI, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+
+                }), win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:false, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create: true}, getDir, fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir never called", Tests.TEST_TIMEOUT);
+        });
+        it("DirectoryEntry.getDirectory: create new dir with space resolveFileSystemURI with encoded URI", function() {
+            var dirName = "de create dir",
+                dirPath = root.fullPath + '/' + dirName,
+                getDir = jasmine.createSpy().andCallFake(function(dirEntry) {
+                    var dirURI = dirEntry.toURL();
+                    // now encode URI and try to resolve
+                    runs(function() {
+                        window.resolveLocalFileSystemURI(encodeURI(dirURI), win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:false, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create: true}, getDir, fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir never called", Tests.TEST_TIMEOUT);
+        });
+
+        it("DirectoryEntry.getDirectory: create new directory", function() {
+            var dirName = "de.create.dir",
+                dirPath = root.fullPath + '/' + dirName,
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:false, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create: true}, win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("DirectoryEntry.getDirectory: create new directory (exclusive)", function() {
+            var dirName = "de.create.exclusive.dir",
+                dirPath = root.fullPath + '/' + dirName,
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+            // create:true, exclusive:true, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create: true, exclusive:true}, win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("DirectoryEntry.getDirectory: create directory that already exists", function() {
+            var dirName = "de.create.existing.dir",
+                dirPath = root.fullPath + '/' + dirName,
+                getDir = jasmine.createSpy().andCallFake(function(directory) {
+                    // create:true, exclusive:false, directory exists
+                    runs(function() {
+                        root.getDirectory(dirName, {create:true}, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create directory to kick off it
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, getDir, this.fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir never called", Tests.TEST_TIMEOUT);
+        });
+        it("DirectoryEntry.getDirectory: create directory that already exists (exclusive)", function() {
+            var dirName = "de.create.exclusive.existing.dir",
+                dirPath = root.fullPath + '/' + dirName,
+                existingDir,
+                getDir = jasmine.createSpy().andCallFake(function(directory) {
+                    existingDir = directory;
+                    // create:true, exclusive:true, directory exists
+                    runs(function() {
+                        root.getDirectory(dirName, {create:true, exclusive:true}, win, fail);
+                    });
+
+                    waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.PATH_EXISTS_ERR);
+
+                    // cleanup
+                    existingDir.remove(null, fail);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create directory to kick off it
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, getDir, fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir never called", Tests.TEST_TIMEOUT);
+        });
+        it("DirectoryEntry.getDirectory: get Entry for existing directory", function() {
+            var dirName = "de.get.dir",
+                dirPath = root.fullPath + '/' + dirName,
+                getDir = jasmine.createSpy().andCallFake(function(directory) {
+                    // create:false, exclusive:false, directory exists
+                    runs(function() {
+                        root.getDirectory(dirName, {create:false}, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create directory to kick off it
+            root.getDirectory(dirName, {create:true}, getDir, fail);
+        });
+        it("DirectoryEntry.getDirectory: get DirectoryEntry for invalid path", function() {
+            var dirName = "de:invalid:path",
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.ENCODING_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create:false, exclusive:false, invalid path
+            runs(function() {
+                root.getDirectory(dirName, {create:false}, win, fail);
+            });
+
+            waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+        it("DirectoryEntry.getDirectory: get DirectoryEntry for existing file", function() {
+            var fileName = "de.existing.file",
+                existingFile,
+                filePath = root.fullPath + '/' + fileName,
+                getDir = jasmine.createSpy().andCallFake(function(file) {
+                    existingFile = file;
+                    // create:false, exclusive:false, existing file
+                    runs(function() {
+                        root.getDirectory(fileName, {create:false}, win, fail);
+                    });
+
+                    waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR);
+
+                    // cleanup
+                    existingFile.remove(null, null);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create file to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, getDir, fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir was called", Tests.TEST_TIMEOUT);
+        });
+        it("DirectoryEntry.getFile: get FileEntry for existing directory", function() {
+            var dirName = "de.existing.dir",
+                existingDir,
+                dirPath = root.fullPath + '/' + dirName,
+                getFile = jasmine.createSpy().andCallFake(function(directory) {
+                    existingDir = directory;
+                    // create:false, exclusive:false, existing directory
+                    runs(function() {
+                        root.getFile(dirName, {create:false}, win, fail);
+                    });
+
+                    waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR);
+
+                    // cleanup
+                    existingDir.remove(null, null);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create directory to kick off it
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, getFile, fail);
+            });
+
+            waitsFor(function() { return getFile.wasCalled; }, "getFile never called", Tests.TEST_TIMEOUT);
+        });
+        it("DirectoryEntry.removeRecursively on directory", function() {
+            var dirName = "de.removeRecursively",
+                subDirName = "dir",
+                dirPath = root.fullPath + '/' + dirName,
+                //subDirPath = this.root.fullPath + '/' + subDirName,
+                subDirPath = dirPath + '/' + subDirName,
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // delete directory
+                    var deleteDirectory = jasmine.createSpy().andCallFake(function(directory) {
+                        runs(function() {
+                            entry.removeRecursively(remove, fail);
+                        });
+
+                        waitsFor(function() { return remove.wasCalled; }, "remove never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a sub-directory within directory
+                    runs(function() {
+                        entry.getDirectory(subDirName, {create: true}, deleteDirectory, fail);
+                    });
+
+                    waitsFor(function() { return deleteDirectory.wasCalled; }, "deleteDirectory never called", Tests.TEST_TIMEOUT);
+                }),
+                remove = jasmine.createSpy().andCallFake(function() {
+                    // it that removed directory no longer exists
+                    runs(function() {
+                        root.getDirectory(dirName, {create:false}, win, dirExists);
+                    });
+
+                    waitsFor(function() { return dirExists.wasCalled; }, "dirExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(dirExists).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                dirExists = jasmine.createSpy().andCallFake(function(error){
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                fail = createFail('DirectoryEntry'),
+                win = createWin('DirectoryEntry');
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("createReader: create reader on existing directory", function() {
+            // create reader for root directory
+            var reader = root.createReader();
+            expect(reader).toBeDefined();
+            expect(typeof reader.readEntries).toBe('function');
+        });
+        it("removeRecursively on root file system", function() {
+            var remove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NO_MODIFICATION_ALLOWED_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // remove root file system
+            runs(function() {
+                root.removeRecursively(win, remove);
+            });
+
+            waitsFor(function() { return remove.wasCalled; }, "remove never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).not.toHaveBeenCalled();
+                expect(remove).toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe('DirectoryReader interface', function() {
+        describe("readEntries", function() {
+            it("should read contents of existing directory", function() {
+                var reader,
+                    win = jasmine.createSpy().andCallFake(function(entries) {
+                        expect(entries).toBeDefined();
+                        expect(entries instanceof Array).toBe(true);
+                    }),
+                    fail = createFail('DirectoryReader');
+
+                // create reader for root directory
+                reader = root.createReader();
+                // read entries
+                runs(function() {
+                    reader.readEntries(win, fail);
+                });
+
+                waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(win).toHaveBeenCalled();
+                    expect(fail).not.toHaveBeenCalled();
+                });
+            });
+            it("should read contents of directory that has been removed", function() {
+                var dirName = "de.createReader.notfound",
+                    dirPath = root.fullPath + '/' + dirName,
+                    entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                        // read entries
+                        var readEntries = jasmine.createSpy().andCallFake(function() {
+                            var reader = directory.createReader();
+
+                            runs(function() {
+                                reader.readEntries(win, itReader);
+                            });
+
+                            waitsFor(function() { return itReader.wasCalled; }, "itReader never called", Tests.TEST_TIMEOUT);
+                        });
+                        // delete directory
+                        runs(function() {
+                            directory.removeRecursively(readEntries, fail);
+                        });
+
+                        waitsFor(function() { return readEntries.wasCalled; }, "readEntries never called", Tests.TEST_TIMEOUT);
+                    }),
+                    itReader = jasmine.createSpy().andCallFake(function(error) {
+                        var itDirectoryExists = jasmine.createSpy().andCallFake(function(error) {
+                            expect(error).toBeDefined();
+                            expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                        });
+
+                        expect(error).toBeDefined();
+                        expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+
+                        runs(function() {
+                            root.getDirectory(dirName, {create:false}, win, itDirectoryExists);
+                        });
+
+                        waitsFor(function() { return itDirectoryExists.wasCalled; }, "itDirectoryExists never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(itDirectoryExists).toHaveBeenCalled();
+                            expect(win).not.toHaveBeenCalled();
+                        });
+                    }),
+                    fail = createFail('DirectoryReader'),
+                    win = createWin('DirectoryReader');
+
+                // create a new directory entry to kick off it
+                runs(function() {
+                    root.getDirectory(dirName, {create:true}, entryCallback, fail);
+                });
+
+                waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+            });
+        });
+    });
+
+    describe('File', function() {
+        it("constructor should be defined", function() {
+            expect(File).toBeDefined();
+            expect(typeof File).toBe('function');
+        });
+        it("should be define File attributes", function() {
+            var file = new File();
+            expect(file.name).toBeDefined();
+            expect(file.fullPath).toBeDefined();
+            expect(file.type).toBeDefined();
+            expect(file.lastModifiedDate).toBeDefined();
+            expect(file.size).toBeDefined();
+        });
+    });
+
+    describe('FileEntry', function() {
+        it("should be define FileEntry methods", function() {
+            var fileName = "fe.methods",
+                itFileEntry = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(typeof fileEntry.createWriter).toBe('function');
+                    expect(typeof fileEntry.file).toBe('function');
+
+                    // cleanup
+                    fileEntry.remove(null, fail);
+                }),
+                fail = createFail('FileEntry');
+
+            // create a new file entry to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, itFileEntry, fail);
+            });
+
+            waitsFor(function() { return itFileEntry.wasCalled; }, "itFileEntry never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itFileEntry).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("createWriter should return a FileWriter object", function() {
+            var fileName = "fe.createWriter",
+                itFile,
+                entryCallback = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    itFile = fileEntry;
+
+                    runs(function() {
+                        fileEntry.createWriter(itWriter, fail);
+                    });
+
+                    waitsFor(function() { return itWriter.wasCalled; }, "itWriter", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itWriter).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itWriter = jasmine.createSpy().andCallFake(function(writer) {
+                    expect(writer).toBeDefined();
+                    expect(writer instanceof FileWriter).toBe(true);
+
+                    // cleanup
+                    itFile.remove(null, fail);
+                }),
+                fail = createFail('FileEntry');
+
+            // create a new file entry to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file should return a File object", function() {
+            var fileName = "fe.file",
+                newFile,
+                entryCallback = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    newFile = fileEntry;
+
+                    runs(function() {
+                        fileEntry.file(itFile, fail);
+                    });
+
+                    waitsFor(function() { return itFile.wasCalled; }, "itFile never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itFile).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itFile = jasmine.createSpy().andCallFake(function(file) {
+                    expect(file).toBeDefined();
+                    expect(file instanceof File).toBe(true);
+
+                    // cleanup
+                    newFile.remove(null, fail);
+                }),
+                fail = createFail('FileEntry');
+
+            // create a new file entry to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file: on File that has been removed", function() {
+            var fileName = "fe.no.file",
+                entryCallback = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    // create File object
+                    var getFile = jasmine.createSpy().andCallFake(function() {
+                        runs(function() {
+                            fileEntry.file(win, itFile);
+                        });
+
+                        waitsFor(function() { return itFile.wasCalled; }, "itFile never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(itFile).toHaveBeenCalled();
+                            expect(win).not.toHaveBeenCalled();
+                        });
+                    });
+                    // delete file
+                    runs(function() {
+                        fileEntry.remove(getFile, fail);
+                    });
+
+                    waitsFor(function() { return getFile.wasCalled; }, "getFile never called", Tests.TEST_TIMEOUT);
+                }),
+                itFile = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                fail = createFail('FileEntry'),
+                win = createWin('FileEntry');
+
+            // create a new file entry to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+    });
+    describe('Entry', function() {
+        it("Entry object", function() {
+            var fileName = "entry",
+                fullPath = root.fullPath + '/' + fileName,
+                fail = createFail('Entry'),
+                itEntry = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toCanonicallyMatch(fileName);
+                    expect(entry.fullPath).toCanonicallyMatch(fullPath);
+                    expect(typeof entry.getMetadata).toBe('function');
+                    expect(typeof entry.setMetadata).toBe('function');
+                    expect(typeof entry.moveTo).toBe('function');
+                    expect(typeof entry.copyTo).toBe('function');
+                    expect(typeof entry.toURL).toBe('function');
+                    expect(typeof entry.remove).toBe('function');
+                    expect(typeof entry.getParent).toBe('function');
+                    expect(typeof entry.createWriter).toBe('function');
+                    expect(typeof entry.file).toBe('function');
+
+                    // cleanup
+                    deleteEntry(fileName);
+                });
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, itEntry, fail);
+            });
+
+            waitsFor(function() { return itEntry.wasCalled; }, "itEntry", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itEntry).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("Entry.getMetadata on file", function() {
+            var fileName = "entry.metadata.file",
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    runs(function() {
+                        entry.getMetadata(itMetadata, fail);
+                    });
+
+                    waitsFor(function() { return itMetadata.wasCalled; }, "itMetadata never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itMetadata).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = createFail('Entry'),
+                itMetadata = jasmine.createSpy().andCallFake(function(metadata) {
+                    expect(metadata).toBeDefined();
+                    expect(metadata.modificationTime instanceof Date).toBe(true);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                });
+
+            // create a new file entry
+            createFile(fileName, entryCallback, fail);
+        });
+        it("Entry.getMetadata on directory", function() {
+            var dirName = "entry.metadata.dir",
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    runs(function() {
+                        entry.getMetadata(itMetadata, fail);
+                    });
+
+                    waitsFor(function() { return itMetadata.wasCalled; }, "itMetadata never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itMetadata).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = createFail('Entry'),
+                itMetadata = jasmine.createSpy().andCallFake(function(metadata) {
+                    expect(metadata).toBeDefined();
+                    expect(metadata.modificationTime instanceof Date).toBe(true);
+
+                    // cleanup
+                    deleteEntry(dirName);
+                });
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("Entry.getParent on file in root file system", function() {
+            var fileName = "entry.parent.file",
+                rootPath = root.fullPath,
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    runs(function() {
+                        entry.getParent(itParent, fail);
+                    });
+
+                    waitsFor(function() { return itParent.wasCalled; }, "itCalled never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itParent).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itParent = jasmine.createSpy().andCallFake(function(parent) {
+                    expect(parent).toBeDefined();
+                    expect(parent.fullPath).toCanonicallyMatch(rootPath);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                });
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("Entry.getParent on directory in root file system", function() {
+            var dirName = "entry.parent.dir",
+                rootPath = root.fullPath,
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    runs(function() {
+                        entry.getParent(itParent, fail);
+                    });
+
+                    waitsFor(function() { return itParent.wasCalled; }, "itParent never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itParent).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itParent = jasmine.createSpy().andCallFake(function(parent) {
+                    expect(parent).toBeDefined();
+                    expect(parent.fullPath).toCanonicallyMatch(rootPath);
+
+                    // cleanup
+                    deleteEntry(dirName);
+                });
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("Entry.getParent on root file system", function() {
+            var rootPath = root.fullPath,
+                itParent = jasmine.createSpy().andCallFake(function(parent) {
+                    expect(parent).toBeDefined();
+                    expect(parent.fullPath).toCanonicallyMatch(rootPath);
+                }),
+                fail = createFail('Entry');
+
+            // create a new directory entry
+            runs(function() {
+                root.getParent(itParent, fail);
+            });
+
+            waitsFor(function() { return itParent.wasCalled; }, "itParent never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itParent).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("Entry.toURL on file", function() {
+            var fileName = "entry.uri.file",
+                rootPath = root.fullPath,
+                itURI = jasmine.createSpy().andCallFake(function(entry) {
+                    var uri = entry.toURL();
+                    expect(uri).toBeDefined();
+                    expect(uri.indexOf(rootPath)).not.toBe(-1);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                }),
+                fail = createFail('Entry');
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, itURI, fail);
+            });
+
+            waitsFor(function() { return itURI.wasCalled; }, "itURI never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itURI).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("Entry.toURL on directory", function() {
+            var dirName = "entry.uri.dir",
+                rootPath = root.fullPath,
+                itURI = jasmine.createSpy().andCallFake(function(entry) {
+                    var uri = entry.toURL();
+                    expect(uri).toBeDefined();
+                    expect(uri.indexOf(rootPath)).not.toBe(-1);
+
+                    // cleanup
+                    deleteEntry(dirName);
+                }),
+                fail = createFail('Entry');
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, itURI, fail);
+            });
+
+            waitsFor(function() { return itURI.wasCalled; }, "itURI never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itURI).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("Entry.remove on file", function() {
+            var fileName = "entry.rm.file",
+                fullPath = root.fullPath + '/' + fileName,
+                win = createWin('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    var checkRemove = jasmine.createSpy().andCallFake(function() {
+                        runs(function() {
+                            root.getFile(fileName, null, win, itRemove);
+                        });
+
+                        waitsFor(function() { return itRemove.wasCalled; }, "itRemove never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(win).not.toHaveBeenCalled();
+                            expect(fail).not.toHaveBeenCalled();
+                            expect(itRemove).toHaveBeenCalled();
+                        });
+                    });
+                    expect(entry).toBeDefined();
+
+                    runs(function() {
+                        entry.remove(checkRemove, fail);
+                    });
+
+                    waitsFor(function() { return checkRemove.wasCalled; }, "checkRemove never called", Tests.TEST_TIMEOUT);
+                }),
+                itRemove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                    // cleanup
+                    deleteEntry(fileName);
+                }),
+                fail = createFail('Entry');
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("remove on empty directory", function() {
+            var dirName = "entry.rm.dir",
+                fullPath = root.fullPath + '/' + dirName,
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    var checkRemove = jasmine.createSpy().andCallFake(function() {
+                        runs(function() {
+                            root.getDirectory(dirName, null, win, itRemove);
+                        });
+
+                        waitsFor(function() { return itRemove.wasCalled; }, "itRemove never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(itRemove).toHaveBeenCalled();
+                            expect(win).not.toHaveBeenCalled();
+                            expect(fail).not.toHaveBeenCalled();
+                        });
+                    });
+
+                    expect(entry).toBeDefined();
+
+                    runs(function() {
+                        entry.remove(checkRemove, fail);
+                    });
+
+                    waitsFor(function() { return checkRemove.wasCalled; }, "checkRemove never called", Tests.TEST_TIMEOUT);
+                }),
+                itRemove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                    // cleanup
+                    deleteEntry(dirName);
+                }),
+                win = createWin('Entry'),
+                fail = createFail('Entry');
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("remove on non-empty directory", function() {
+            var dirName = "entry.rm.dir.not.empty",
+                fullPath = root.fullPath + '/' + dirName,
+                fileName = "remove.txt",
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    var checkFile = jasmine.createSpy().andCallFake(function(error) {
+                        expect(error).toBeDefined();
+                        expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+                        // verify that dir still exists
+                        runs(function() {
+                            root.getDirectory(dirName, null, itRemove, fail);
+                        });
+
+                        waitsFor(function() { return itRemove.wasCalled; }, "itRemove never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(win).not.toHaveBeenCalled();
+                            expect(fail).not.toHaveBeenCalled();
+                            expect(itRemove).toHaveBeenCalled();
+                        });
+                    });
+                    // delete directory
+                    var deleteDirectory = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        runs(function() {
+                            entry.remove(win, checkFile);
+                        });
+
+                        waitsFor(function() { return checkFile.wasCalled; }, "checkFile never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a file within directory, then try to delete directory
+                    runs(function() {
+                        entry.getFile(fileName, {create: true}, deleteDirectory, fail);
+                    });
+
+                    waitsFor(function() { return deleteDirectory.wasCalled; }, "deleteDirectory never called", Tests.TEST_TIMEOUT);
+                }),
+                itRemove = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.fullPath).toCanonicallyMatch(fullPath);
+                    // cleanup
+                    deleteEntry(dirName);
+                }),
+                win = createWin('Entry'),
+                fail = createFail('Entry');
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("remove on root file system", function() {
+            var itRemove = jasmine.createSpy().andCallFake(function(error) {
+                expect(error).toBeDefined();
+                expect(error).toBeFileError(FileError.NO_MODIFICATION_ALLOWED_ERR);
+            }),
+            win = createWin('Entry');
+
+            // remove entry that doesn't exist
+            runs(function() {
+                root.remove(win, itRemove);
+            });
+
+            waitsFor(function() { return itRemove.wasCalled; }, "itRemove never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).not.toHaveBeenCalled();
+                expect(itRemove).toHaveBeenCalled();
+            });
+        });
+        it("copyTo: file", function() {
+            var file1 = "entry.copy.file1",
+                file2 = "entry.copy.file2",
+                fullPath = root.fullPath + '/' + file2,
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // copy file1 to file2
+                    runs(function() {
+                        entry.copyTo(root, file2, itCopy, fail);
+                    });
+
+                    waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.fullPath).toCanonicallyMatch(fullPath);
+                    expect(entry.name).toCanonicallyMatch(file2);
+
+                    runs(function() {
+                        root.getFile(file2, {create:false}, itFileExists, fail);
+                    });
+
+                    waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(itFileExists).toHaveBeenCalled();
+                    });
+                }),
+                itFileExists = jasmine.createSpy().andCallFake(function(entry2) {
+                    // a bit redundant since copy returned this entry already
+                    expect(entry2).toBeDefined();
+                    expect(entry2.isFile).toBe(true);
+                    expect(entry2.isDirectory).toBe(false);
+                    expect(entry2.fullPath).toCanonicallyMatch(fullPath);
+                    expect(entry2.name).toCanonicallyMatch(file2);
+
+                    // cleanup
+                    deleteEntry(file1);
+                    deleteEntry(file2);
+                });
+
+            // create a new file entry to kick off it
+            runs(function() {
+                createFile(file1, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("copyTo: file onto itself", function() {
+            var file1 = "entry.copy.fos.file1",
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // copy file1 onto itself
+                    runs(function() {
+                        entry.copyTo(root, null, win, itCopy);
+                    });
+
+                    waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itCopy).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = createFail('Entry'),
+                win = createWin('Entry'),
+                itCopy = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+
+                    // cleanup
+                    deleteEntry(file1);
+                });
+
+            // create a new file entry to kick off it
+            runs(function() {
+                createFile(file1, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("copyTo: directory", function() {
+            var file1 = "file1",
+                srcDir = "entry.copy.srcDir",
+                dstDir = "entry.copy.dstDir",
+                dstPath = root.fullPath + '/' + dstDir,
+                filePath = dstPath + '/' + file1,
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    var copyDir = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        // copy srcDir to dstDir
+                        runs(function() {
+                            directory.copyTo(root, dstDir, itCopy, fail);
+                        });
+
+                        waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+                    });
+
+                    // create a file within new directory
+                    runs(function() {
+                        directory.getFile(file1, {create: true}, copyDir, fail);
+                    });
+
+                    waitsFor(function() { return copyDir.wasCalled; }, "copyDir never called", Tests.TEST_TIMEOUT);
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.fullPath).toCanonicallyMatch(dstPath);
+                    expect(directory.name).toCanonicallyMatch(dstDir);
+
+                    runs(function() {
+                        root.getDirectory(dstDir, {create:false}, itDirExists, fail);
+                    });
+
+                    waitsFor(function() { return itDirExists.wasCalled; }, "itDirExists never called", Tests.TEST_TIMEOUT);
+                }),
+                itDirExists = jasmine.createSpy().andCallFake(function(dirEntry) {
+                     expect(dirEntry).toBeDefined();
+                     expect(dirEntry.isFile).toBe(false);
+                     expect(dirEntry.isDirectory).toBe(true);
+                     expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+                     expect(dirEntry.name).toCanonicallyMatch(dstDir);
+
+                     runs(function() {
+                         dirEntry.getFile(file1, {create:false}, itFileExists, fail);
+                     });
+
+                     waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+                     runs(function() {
+                         expect(itFileExists).toHaveBeenCalled();
+                         expect(fail).not.toHaveBeenCalled();
+                     });
+                }),
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.isFile).toBe(true);
+                    expect(fileEntry.isDirectory).toBe(false);
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+                    expect(fileEntry.name).toCanonicallyMatch(file1);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                    deleteEntry(dstDir);
+                }),
+                fail = createFail('Entry');
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createDirectory(srcDir, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("copyTo: directory to backup at same root directory", function() {
+            var file1 = "file1",
+                srcDir = "entry.copy.srcDirSame",
+                dstDir = "entry.copy.srcDirSame-backup",
+                dstPath = root.fullPath + '/' + dstDir,
+                filePath = dstPath + '/' + file1,
+                fail = createFail('Entry copyTo: directory to backup at same root'),
+                entryCallback = function(directory) {
+                    var copyDir = function(fileEntry) {
+                        // copy srcDir to dstDir
+                        directory.copyTo(root, dstDir, itCopy, fail);
+                    };
+                    // create a file within new directory
+                    directory.getFile(file1, {create: true}, copyDir, fail);
+                },
+                itCopy = function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.fullPath).toCanonicallyMatch(dstPath);
+                    expect(directory.name).toCanonicallyMatch(dstDir);
+
+                    root.getDirectory(dstDir, {create:false}, itDirExists, fail);
+                },
+                itDirExists = function(dirEntry) {
+                     expect(dirEntry).toBeDefined();
+                     expect(dirEntry.isFile).toBe(false);
+                     expect(dirEntry.isDirectory).toBe(true);
+                     expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+                     expect(dirEntry.name).toCanonicallyMatch(dstDir);
+
+                     dirEntry.getFile(file1, {create:false}, itFileExists, fail);
+                },
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    var cleanSrc = jasmine.createSpy();
+                    var cleanDst = jasmine.createSpy();
+                    runs(function() {
+                        expect(fileEntry).toBeDefined();
+                        expect(fileEntry.isFile).toBe(true);
+                        expect(fileEntry.isDirectory).toBe(false);
+                        expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+                        expect(fileEntry.name).toCanonicallyMatch(file1);
+                        expect(fail).not.toHaveBeenCalled();
+
+                        // cleanup
+                        deleteEntry(srcDir, cleanSrc);
+                        deleteEntry(dstDir, cleanDst);
+                    });
+
+                    waitsFor(function() { return cleanSrc.wasCalled && cleanDst.wasCalled; }, "cleanSrc and cleanDst cleanup methods", Tests.TEST_TIMEOUT);
+                });
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createDirectory(srcDir, entryCallback, fail);
+            });
+
+            waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists", 10000);
+        });
+        it("copyTo: directory onto itself", function() {
+            var file1 = "file1",
+                srcDir = "entry.copy.dos.srcDir",
+                srcPath = root.fullPath + '/' + srcDir,
+                filePath = srcPath + '/' + file1,
+                win = createWin('Entry'),
+                fail = createFail('Entry copyTo: directory onto itself'),
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    var copyDir = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        // copy srcDir onto itself
+                        runs(function() {
+                            directory.copyTo(root, null, win, itCopy);
+                        });
+
+                        waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a file within new directory
+                    runs(function() {
+                        directory.getFile(file1, {create: true}, copyDir, fail);
+                    });
+
+                    waitsFor(function() { return copyDir.wasCalled; }, "copyDir never called", Tests.TEST_TIMEOUT);
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+
+                    runs(function() {
+                        root.getDirectory(srcDir, {create:false}, itDirectoryExists, fail);
+                    });
+
+                    waitsFor(function() { return itDirectoryExists.wasCalled; }, "itDirectoryExists", Tests.TEST_TIMEOUT);
+                }),
+                itDirectoryExists = jasmine.createSpy().andCallFake(function(dirEntry) {
+                    // returning confirms existence so just check fullPath entry
+                    expect(dirEntry).toBeDefined();
+                    expect(dirEntry.fullPath).toCanonicallyMatch(srcPath);
+
+                    runs(function() {
+                        dirEntry.getFile(file1, {create:false}, itFileExists, fail);
+                    });
+
+                    waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(itFileExists).toHaveBeenCalled();
+                    });
+                }),
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                });
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createDirectory(srcDir, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("copyTo: directory into itself", function() {
+            var srcDir = "entry.copy.dis.srcDir",
+                dstDir = "entry.copy.dis.dstDir",
+                fail = createFail('Entry'),
+                win = createWin('Entry'),
+                srcPath = root.fullPath + '/' + srcDir,
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    // copy source directory into itself
+                    runs(function() {
+                        directory.copyTo(directory, dstDir, win, itCopy);
+                    });
+
+                    waitsFor(function() { return itCopy.wasCalled; }, "itCopy", Tests.TEST_TIMEOUT);
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+
+                    runs(function() {
+                        root.getDirectory(srcDir, {create:false}, itDirectoryExists, fail);
+                    });
+
+                    waitsFor(function() { return itDirectoryExists.wasCalled; }, "itDirectoryExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itDirectoryExists).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itDirectoryExists = jasmine.createSpy().andCallFake(function(dirEntry) {
+                    // returning confirms existence so just check fullPath entry
+                    expect(dirEntry).toBeDefined();
+                    expect(dirEntry.fullPath).toCanonicallyMatch(srcPath);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                });
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createDirectory(srcDir, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("copyTo: directory that does not exist", function() {
+            var file1 = "entry.copy.dnf.file1",
+                dstDir = "entry.copy.dnf.dstDir",
+                filePath = root.fullPath + '/' + file1,
+                dstPath = root.fullPath + '/' + dstDir,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // copy file to target directory that does not exist
+                    runs(function() {
+                        directory = new DirectoryEntry();
+                        directory.fullPath = dstPath;
+                        entry.copyTo(directory, null, win, itCopy);
+                    });
+
+                    waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                    runs(function() {
+                        root.getFile(file1, {create: false}, itFileExists, fail);
+                    });
+
+                    waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itFileExists).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+
+                    // cleanup
+                    deleteEntry(file1);
+                });
+
+            // create a new file entry to kick off it
+            runs(function() {
+                createFile(file1, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("copyTo: invalid target name", function() {
+            var file1 = "entry.copy.itn.file1",
+                file2 = "bad:file:name",
+                filePath = root.fullPath + '/' + file1,
+                fail = createFail('Entry'),
+                win = createWin('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // copy file1 to file2
+                    runs(function() {
+                        entry.copyTo(root, file2, win, itCopy);
+                    });
+
+                    waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                        expect(itCopy).toHaveBeenCalled();
+                    });
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.ENCODING_ERR);
+
+                    // cleanup
+                    deleteEntry(file1);
+                });
+
+            // create a new file entry
+            runs(function() {
+                createFile(file1, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("moveTo: file to same parent", function() {
+            var file1 = "entry.move.fsp.file1",
+                file2 = "entry.move.fsp.file2",
+                srcPath = root.fullPath + '/' + file1,
+                dstPath = root.fullPath + '/' + file2,
+                fail = createFail('Entry'),
+                win = createWin('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // move file1 to file2
+                    runs(function() {
+                        entry.moveTo(root, file2, itMove, fail);
+                    });
+
+                    waitsFor(function() { return itMove.wasCalled; }, "itMove never called", Tests.TEST_TIMEOUT);
+                }),
+                itMove = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.fullPath).toCanonicallyMatch(dstPath);
+                    expect(entry.name).toCanonicallyMatch(file2);
+
+                    runs(function() {
+                        root.getFile(file2, {create:false}, itMovedExists, fail);
+                    });
+
+                    waitsFor(function() { return itMovedExists.wasCalled; }, "itMovedExists never called", Tests.TEST_TIMEOUT);
+                }),
+                itMovedExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(dstPath);
+
+                    runs(function() {
+                        root.getFile(file1, {create:false}, win, itOrig);
+                    });
+
+                    waitsFor(function() { return itOrig.wasCalled; }, "itOrig never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(itOrig).toHaveBeenCalled();
+                    });
+                }),
+                itOrig = jasmine.createSpy().andCallFake(function(error) {
+                    //expect(navigator.fileMgr.itFileExists(srcPath) === false, "original file should not exist.");
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+
+                    // cleanup
+                    deleteEntry(file1);
+                    deleteEntry(file2);
+                });
+
+            // create a new file entry to kick off it
+            runs(function() {
+                createFile(file1, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("moveTo: file to new parent", function() {
+            var file1 = "entry.move.fnp.file1",
+                dir = "entry.move.fnp.dir",
+                srcPath = root.fullPath + '/' + file1,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                dstPath = root.fullPath + '/' + dir + '/' + file1,
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // move file1 to new directory
+                    var moveFile = jasmine.createSpy().andCallFake(function(directory) {
+                        var itMove = jasmine.createSpy().andCallFake(function(entry) {
+                            expect(entry).toBeDefined();
+                            expect(entry.isFile).toBe(true);
+                            expect(entry.isDirectory).toBe(false);
+                            expect(entry.fullPath).toCanonicallyMatch(dstPath);
+                            expect(entry.name).toCanonicallyMatch(file1);
+                            // it the moved file exists
+                            runs(function() {
+                                directory.getFile(file1, {create:false}, itMovedExists, fail);
+                            });
+
+                            waitsFor(function() { return itMovedExists.wasCalled; }, "itMovedExists never called", Tests.TEST_TIMEOUT);
+                        });
+                        // move the file
+                        runs(function() {
+                            entry.moveTo(directory, null, itMove, fail);
+                        });
+
+                        waitsFor(function() { return itMove.wasCalled; }, "itMove never called", Tests.TEST_TIMEOUT);
+                    });
+
+                    // create a parent directory to move file to
+                    runs(function() {
+                        root.getDirectory(dir, {create: true}, moveFile, fail);
+                    });
+
+                    waitsFor(function() { return moveFile.wasCalled; }, "moveFile never called", Tests.TEST_TIMEOUT);
+                }),
+                itMovedExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(dstPath);
+
+                    runs(function() {
+                        root.getFile(file1, {create:false}, win, itOrig);
+                    });
+
+                    waitsFor(function() { return itOrig.wasCalled; }, "itOrig never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(itOrig).toHaveBeenCalled();
+                    });
+                }),
+                itOrig = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+
+                    // cleanup
+                    deleteEntry(file1);
+                    deleteEntry(dir);
+                });
+
+            // ensure destination directory is cleaned up first
+            runs(function() {
+                deleteEntry(dir, function() {
+                    // create a new file entry to kick off it
+                    createFile(file1, entryCallback, fail);
+                }, fail);
+            });
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("moveTo: directory to same parent", function() {
+            var file1 = "file1",
+                srcDir = "entry.move.dsp.srcDir",
+                dstDir = "entry.move.dsp.dstDir",
+                srcPath = root.fullPath + '/' + srcDir,
+                dstPath = root.fullPath + '/' + dstDir,
+                filePath = dstPath + '/' + file1,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    var moveDir = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        // move srcDir to dstDir
+                        runs(function() {
+                            directory.moveTo(root, dstDir, itMove, fail);
+                        });
+
+                        waitsFor(function() { return itMove.wasCalled; }, "itMove never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a file within directory
+                    runs(function() {
+                        directory.getFile(file1, {create: true}, moveDir, fail);
+                    });
+
+                    waitsFor(function() { return moveDir.wasCalled; }, "moveDir never called", Tests.TEST_TIMEOUT);
+                }),
+                itMove = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.fullPath).toCanonicallyMatch(dstPath);
+                    expect(directory.name).toCanonicallyMatch(dstDir);
+                    // it that moved file exists in destination dir
+
+                    runs(function() {
+                        directory.getFile(file1, {create:false}, itMovedExists, fail);
+                    });
+
+                    waitsFor(function() { return itMovedExists.wasCalled; }, "itMovedExists never called", Tests.TEST_TIMEOUT);
+                }),
+                itMovedExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+
+                    // check that the moved file no longer exists in original dir
+                    runs(function() {
+                        root.getFile(file1, {create:false}, win, itOrig);
+                    });
+
+                    waitsFor(function() { return itOrig.wasCalled; }, "itOrig never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(itOrig).toHaveBeenCalled();
+                    });
+                }),
+                itOrig = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                    deleteEntry(dstDir);
+                });
+
+            // ensure destination directory is cleaned up before it
+            runs(function() {
+                deleteEntry(dstDir, function() {
+                    // create a new directory entry to kick off it
+                    createDirectory(srcDir, entryCallback, fail);
+                }, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCAllback never called", Tests.TEST_TIMEOUT);
+        });
+        it("moveTo: directory to same parent with same name", function() {
+            var file1 = "file1",
+                srcDir = "entry.move.dsp.srcDir",
+                dstDir = "entry.move.dsp.srcDir-backup",
+                srcPath = root.fullPath + '/' + srcDir,
+                dstPath = root.fullPath + '/' + dstDir,
+                filePath = dstPath + '/' + file1,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    var moveDir = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        // move srcDir to dstDir
+                        runs(function() {
+                            directory.moveTo(root, dstDir, itMove, fail);
+                        });
+
+                        waitsFor(function() { return itMove.wasCalled; }, "itMove never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a file within directory
+                    runs(function() {
+                        directory.getFile(file1, {create: true}, moveDir, fail);
+                    });
+
+                    waitsFor(function() { return moveDir.wasCalled; }, "moveDir never called", Tests.TEST_TIMEOUT);
+                }),
+                itMove = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.fullPath).toCanonicallyMatch(dstPath);
+                    expect(directory.name).toCanonicallyMatch(dstDir);
+                    // check that moved file exists in destination dir
+                    runs(function() {
+                        directory.getFile(file1, {create:false}, itMovedExists, null);
+                    });
+
+                    waitsFor(function() { return itMovedExists.wasCalled; }, "itMovedExists never called", Tests.TEST_TIMEOUT);
+                }),
+                itMovedExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+                    // check that the moved file no longer exists in original dir
+                    runs(function() {
+                        root.getFile(file1, {create:false}, win, itOrig);
+                    });
+
+                    waitsFor(function() { return itOrig.wasCalled; }, "itOrig never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(itOrig).toHaveBeenCalled();
+                    });
+                }),
+                itOrig = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                    deleteEntry(dstDir);
+                });
+
+            // ensure destination directory is cleaned up before it
+            runs(function() {
+                deleteEntry(dstDir, function() {
+                    // create a new directory entry to kick off it
+                    createDirectory(srcDir, entryCallback, fail);
+                }, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("moveTo: directory to new parent", function() {
+            var file1 = "file1",
+                srcDir = "entry.move.dnp.srcDir",
+                dstDir = "entry.move.dnp.dstDir",
+                srcPath = root.fullPath + '/' + srcDir,
+                dstPath = root.fullPath + '/' + dstDir,
+                filePath = dstPath + '/' + file1,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    var moveDir = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        // move srcDir to dstDir
+                        runs(function() {
+                            directory.moveTo(root, dstDir, itMove, fail);
+                        });
+
+                        waitsFor(function() { return itMove.wasCalled; }, "itMove never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a file within directory
+                    runs(function() {
+                        directory.getFile(file1, {create: true}, moveDir, fail);
+                    });
+
+                    waitsFor(function() { return moveDir.wasCalled; }, "moveDir never called", Tests.TEST_TIMEOUT);
+                }),
+                itMove = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.fullPath).toCanonicallyMatch(dstPath);
+                    expect(directory.name).toCanonicallyMatch(dstDir);
+                    // it that moved file exists in destination dir
+                    runs(function() {
+                        directory.getFile(file1, {create:false}, itMovedExists, fail);
+                    });
+
+                    waitsFor(function() { return itMovedExists.wasCalled; }, "itMovedExists never called", Tests.TEST_TIMEOUT);
+                }),
+                itMovedExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+                    // it that the moved file no longer exists in original dir
+                    runs(function() {
+                        root.getFile(file1, {create:false}, win, itOrig);
+                    });
+
+                    waitsFor(function() { return itOrig.wasCalled; }, "itOrig never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(itOrig).toHaveBeenCalled();
+                    });
+                }),
+                itOrig = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                    deleteEntry(dstDir);
+                });
+
+            // ensure destination directory is cleaned up before it
+            runs(function() {
+                deleteEntry(dstDir, function() {
+                    // create a new directory entry to kick off it
+                    createDirectory(srcDir, entryCallback, fail);
+                }, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("moveTo: directory onto itself", function() {
+            var file1 = "file1",
+                srcDir = "entry.move.dos.srcDir",
+                srcPath = root.fullPath + '/' + srcDir,
+                filePath = srcPath + '/' + file1,
+                fail = createFail('Entry'),
+                win = createWin('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    var moveDir = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        // move srcDir onto itself
+                        runs(function() {
+                            directory.moveTo(root, null, win, itMove);
+                        });
+
+                        waitsFor(function() { return itMove.wasCalled; }, "itMove never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a file within new directory
+                    runs(function() {
+                        directory.getFile(file1, {create: true}, moveDir, fail);
+                    });
+
+                    waitsFor(function() { return moveDir.wasCalled; }, "moveDir never called", Tests.TEST_TIMEOUT);
+                }),
+                itMove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+
+                    // it that original dir still exists
+                    runs(function() {
+                        root.getDirectory(srcDir, {create:false}, itDirectoryExists, fail);
+                    });
+
+                    waitsFor(function() { return itDirectoryExists.wasCalled; }, "itDirectoryExists", Tests.TEST_TIMEOUT);
+                }),
+                itDirectoryExists = jasmine.createSpy().andCallFake(function(dirEntry) {
+                    // returning confirms existence so just check fullPath entry
+                    expect(dirEntry).toBeDefined();
+                    expect(dirEntry.fullPath).toCanonicallyMatch(srcPath);
+
+                    runs(function() {
+                        dirEntry.getFile(file1, {create:false}, itFileExists, fail);
+                    });
+
+                    waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itFileExists).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                });
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createDirectory(srcDir, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("moveTo: directory into itself", function() {
+            var srcDir = "entry.move.dis.srcDir",
+                dstDir = "entry.move.dis.dstDir",
+                srcPath = root.fullPath + '/' + srcDir,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    // move source directory into itself
+                    runs(function() {
+                        directory.moveTo(directory, dstDir, win, itMove);
+                    });
+
+                    waitsFor(function() { return itMove.wasCalled; }, "itMove never called", Tests.TEST_TIMEOUT);
+                }),
+                itMove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+                    // make sure original directory still exists
+                    runs(function() {
+                        root.getDirectory(srcDir, {create:false}, itDirectoryExists, fail);
+                    });
+
+                    waitsFor(function() { return itDirectoryExists.wasCalled; }, "itDirectoryExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                        expect(itDirectoryExists).toHaveBeenCalled();
+                    });
+                }),
+                itDirectoryExists = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.fullPath).toCanonicallyMatch(srcPath);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                });
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createDirectory(srcDir, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("moveTo: file onto itself", function() {
+            var file1 = "entry.move.fos.file1",
+                filePath = root.fullPath + '/' + file1,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // move file1 onto itself
+                    runs(function() {
+                        entry.moveTo(root, null, win, itMove);
+                    });
+
+                    waitsFor(function() { return itMove.wasCalled; }, "itMove never called", Tests.TEST_TIMEOUT);
+                }),
+                itMove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+
+                    //it that original file still exists
+                    runs(function() {
+                        root.getFile(file1, {create:false}, itFileExists, fail);
+                    });
+
+                    waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itFileExists).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+
+                    // cleanup
+                    deleteEntry(file1);
+                });
+
+            // create a new file entry to kick off it
+            runs(function() {
+                createFile(file1, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCAllback never called", Tests.TEST_TIMEOUT);
+        });
+        it("moveTo: file onto existing directory", function() {
+            var file1 = "entry.move.fod.file1",
+                dstDir = "entry.move.fod.dstDir",
+                subDir = "subDir",
+                dirPath = root.fullPath + '/' + dstDir + '/' + subDir,
+                filePath = root.fullPath + '/' + file1,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = function(entry) {
+                    var createSubDirectory = function(directory) {
+                        var moveFile = function(subDirectory) {
+                            var itMove = function(error) {
+                                expect(error).toBeDefined();
+                                expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+                                // check that original dir still exists
+                                directory.getDirectory(subDir, {create:false}, itDirectoryExists, fail);
+                            };
+                            // move file1 onto sub-directory
+                            entry.moveTo(directory, subDir, win, itMove);
+                        };
+                        // create sub-directory
+                        directory.getDirectory(subDir, {create: true}, moveFile, fail);
+                    };
+                    // create top level directory
+                    root.getDirectory(dstDir, {create: true}, createSubDirectory, fail);
+                },
+                itDirectoryExists = function(dirEntry) {
+                    expect(dirEntry).toBeDefined();
+                    expect(dirEntry.fullPath).toCanonicallyMatch(dirPath);
+                    // check that original file still exists
+                    root.getFile(file1, {create:false},itFileExists, fail);
+                },
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+
+                    // cleanup
+                    deleteEntry(file1);
+                    deleteEntry(dstDir);
+                });
+
+            // ensure destination directory is cleaned up before it
+            runs(function() {
+                deleteEntry(dstDir, function() {
+                    // create a new file entry to kick off it
+                    createFile(file1, entryCallback, fail);
+                }, fail);
+            });
+
+            waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itFileExists).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("moveTo: directory onto existing file", function() {
+            var file1 = "entry.move.dof.file1",
+                srcDir = "entry.move.dof.srcDir",
+                dirPath = root.fullPath + '/' + srcDir,
+                filePath = root.fullPath + '/' + file1,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = function(entry) {
+                    var moveDir = function(fileEntry) {
+                        // move directory onto file
+                        entry.moveTo(root, file1, win, itMove);
+                    };
+                    // create file
+                    root.getFile(file1, {create: true}, moveDir, fail);
+                },
+                itMove = function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+                    // it that original directory exists
+                    root.getDirectory(srcDir, {create:false}, itDirectoryExists, fail);
+                },
+                itDirectoryExists = function(dirEntry) {
+                    // returning confirms existence so just check fullPath entry
+                    expect(dirEntry).toBeDefined();
+                    expect(dirEntry.fullPath).toCanonicallyMatch(dirPath);
+                    // it that original file exists
+                    root.getFile(file1, {create:false}, itFileExists, fail);
+                },
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+
+                    // cleanup
+                    deleteEntry(file1);
+                    deleteEntry(srcDir);
+                });
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createDirectory(srcDir, entryCallback, fail);
+            });
+
+            waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itFileExists).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("copyTo: directory onto existing file", function() {
+            var file1 = "entry.copy.dof.file1",
+                srcDir = "entry.copy.dof.srcDir",
+                dirPath = root.fullPath + '/' + srcDir,
+                filePath = root.fullPath + '/' + file1,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = function(entry) {
+                    var copyDir = function(fileEntry) {
+                        // move directory onto file
+                        entry.copyTo(root, file1, win, itMove);
+                    };
+                    // create file
+                    root.getFile(file1, {create: true}, copyDir, fail);
+                },
+                itMove = function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+                    //check that original dir still exists
+                    root.getDirectory(srcDir, {create:false}, itDirectoryExists, fail);
+                },
+                itDirectoryExists = function(dirEntry) {
+                    // returning confirms existence so just check fullPath entry
+                    expect(dirEntry).toBeDefined();
+                    expect(dirEntry.fullPath).toCanonicallyMatch(dirPath);
+                    // it that original file still exists
+                    root.getFile(file1, {create:false}, itFileExists, fail);
+                },
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+
+                    // cleanup
+                    deleteEntry(file1);
+                    deleteEntry(srcDir);
+                });
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createDirectory(srcDir, entryCallback, fail);
+            });
+
+            waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itFileExists).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("moveTo: directory onto directory that is not empty", function() {
+            var srcDir = "entry.move.dod.srcDir",
+                dstDir = "entry.move.dod.dstDir",
+                subDir = "subDir",
+                srcPath = root.fullPath + '/' + srcDir,
+                dstPath = root.fullPath + '/' + dstDir + '/' + subDir,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = function(entry) {
+                    var createSubDirectory = function(directory) {
+                        var moveDir = function(subDirectory) {
+                            // move srcDir onto dstDir (not empty)
+                            entry.moveTo(root, dstDir, win, itMove);
+                        };
+                        var itMove = function(error) {
+                            expect(error).toBeDefined();
+                            expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+
+                            // it that destination directory still exists
+                            directory.getDirectory(subDir, {create:false}, itDirectoryExists, fail);
+                        };
+                        // create sub-directory
+                        directory.getDirectory(subDir, {create: true}, moveDir, fail);
+                    };
+                    // create top level directory
+                    root.getDirectory(dstDir, {create: true}, createSubDirectory, fail);
+                },
+                itDirectoryExists = function(dirEntry) {
+                    // returning confirms existence so just check fullPath entry
+                    expect(dirEntry).toBeDefined();
+                    expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+                    // it that source directory exists
+                    root.getDirectory(srcDir,{create:false}, itSrcDirectoryExists, fail);
+                },
+                itSrcDirectoryExists = jasmine.createSpy().andCallFake(function(srcEntry){
+                    expect(srcEntry).toBeDefined();
+                    expect(srcEntry.fullPath).toCanonicallyMatch(srcPath);
+                    // cleanup
+                    deleteEntry(srcDir);
+                    deleteEntry(dstDir);
+                });
+
+            // ensure destination directory is cleaned up before it
+            runs(function() {
+                deleteEntry(dstDir, function() {
+                    // create a new file entry to kick off it
+                    createDirectory(srcDir, entryCallback, fail);
+                }, fail);
+            });
+
+            waitsFor(function() { return itSrcDirectoryExists.wasCalled; }, "itSrcDirectoryExists never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itSrcDirectoryExists).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("moveTo: file replace existing file", function() {
+            var file1 = "entry.move.frf.file1",
+                file2 = "entry.move.frf.file2",
+                file1Path = root.fullPath + '/' + file1,
+                file2Path = root.fullPath + '/' + file2,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = function(entry) {
+                    var moveFile = function(fileEntry) {
+                        // replace file2 with file1
+                        entry.moveTo(root, file2, itMove, fail);
+                    };
+                    // create file
+                    root.getFile(file2, {create: true}, moveFile,fail);
+                },
+                itMove = function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.fullPath).toCanonicallyMatch(file2Path);
+                    expect(entry.name).toCanonicallyMatch(file2);
+
+                    // it that old file does not exists
+                    root.getFile(file1, {create:false}, win, itFileMoved);
+                },
+                itFileMoved = function(error){
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                    // it that new file exists
+                    root.getFile(file2, {create:false}, itFileExists, fail);
+                },
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(file2Path);
+
+                    // cleanup
+                    deleteEntry(file1);
+                    deleteEntry(file2);
+                });
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                createFile(file1, entryCallback, fail);
+            });
+
+            waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itFileExists).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("moveTo: directory replace empty directory", function() {
+            var file1 = "file1",
+                srcDir = "entry.move.drd.srcDir",
+                dstDir = "entry.move.drd.dstDir",
+                srcPath = root.fullPath + '/' + srcDir,
+                dstPath = root.fullPath + '/' + dstDir,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                filePath = dstPath + '/' + file1,
+                entryCallback = function(directory) {
+                    var mkdir = function(fileEntry) {
+                        // create destination directory
+                        root.getDirectory(dstDir, {create: true}, moveDir, fail);
+                    };
+                    var moveDir = function(fileEntry) {
+                        // move srcDir to dstDir
+                        directory.moveTo(root, dstDir, itMove, fail);
+                    };
+                    // create a file within source directory
+                    directory.getFile(file1, {create: true}, mkdir, fail);
+                },
+                itMove = function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.fullPath).toCanonicallyMatch(dstPath);
+                    expect(directory.name).toCanonicallyMatch(dstDir);
+                    // check that old directory contents have been moved
+                    directory.getFile(file1, {create:false}, itFileExists, fail);
+                },
+                itFileExists = function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+
+                    // check that old directory no longer exists
+                    root.getDirectory(srcDir, {create:false}, win, itRemoved);
+                },
+                itRemoved = jasmine.createSpy().andCallFake(function(error){
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                    deleteEntry(dstDir);
+                });
+
+            // ensure destination directory is cleaned up before it
+            runs(function() {
+                deleteEntry(dstDir, function() {
+                    // create a new directory entry to kick off it
+                    createDirectory(srcDir, entryCallback, fail);
+                }, fail);
+            });
+
+            waitsFor(function() { return itRemoved.wasCalled; }, "itRemoved never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itRemoved).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("moveTo: directory that does not exist", function() {
+            var file1 = "entry.move.dnf.file1",
+                dstDir = "entry.move.dnf.dstDir",
+                filePath = root.fullPath + '/' + file1,
+                dstPath = root.fullPath + '/' + dstDir,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = function(entry) {
+                    // move file to directory that does not exist
+                    directory = new DirectoryEntry();
+                    directory.fullPath = dstPath;
+                    entry.moveTo(directory, null, win, itMove);
+                },
+                itMove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+
+                    // cleanup
+                    deleteEntry(file1);
+                });
+
+            // create a new file entry to kick off it
+            runs(function() {
+                createFile(file1, entryCallback, fail);
+            });
+
+            waitsFor(function() { return itMove.wasCalled; }, "itMove never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itMove).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("moveTo: invalid target name", function() {
+            var file1 = "entry.move.itn.file1",
+                file2 = "bad:file:name",
+                filePath = root.fullPath + '/' + file1,
+                win = createWin('Entry'),
+                fail = createFail('Entry'),
+                entryCallback = function(entry) {
+                    // move file1 to file2
+                    entry.moveTo(root, file2, win, itMove);
+                },
+                itMove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.ENCODING_ERR);
+
+                    // cleanup
+                    deleteEntry(file1);
+                });
+
+            // create a new file entry to kick off it
+            runs(function() {
+                createFile(file1,entryCallback, fail);
+            });
+
+            waitsFor(function() { return itMove.wasCalled; }, "itMove never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itMove).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe('FileReader', function() {
+        it("should have correct methods", function() {
+            var reader = new FileReader();
+            expect(reader).toBeDefined();
+            expect(typeof reader.readAsBinaryString).toBe('function');
+            expect(typeof reader.readAsDataURL).toBe('function');
+            expect(typeof reader.readAsText).toBe('function');
+            expect(typeof reader.readAsArrayBuffer).toBe('function');
+            expect(typeof reader.abort).toBe('function');
+        });
+    });
+
+    describe('read method', function(){
+        it("should read file properly, File object", function() {
+            // path of file
+            var fileName = "reader.txt",
+                // file content
+                rule = "There is an exception to every rule.  Except this one.",
+                verifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(evt).toBeDefined();
+                    expect(evt.target.result).toBe(rule);
+                }),
+                fail = createFail('FileReader'),
+                filePath = root.fullPath + '/' + fileName;
+                // creates a FileWriter object
+                create_writer = function(fileEntry) {
+                    fileEntry.createWriter(write_file, fail);
+                },
+                // writes file and reads it back in
+                write_file = function(writer) {
+                    writer.onwriteend = read_file;
+                    writer.write(rule);
+                },
+                // reads file and compares content to what was written
+                read_file = function(evt) {
+                    var reader = new FileReader();
+                    reader.onloadend = verifier;
+                    var myFile = new File();
+
+                    myFile.fullPath = filePath;
+                    reader.readAsText(myFile);
+                };
+
+            // create a file, write to it, and read it in again
+            runs(function() {
+                root.getFile(fileName, {create: true}, create_writer, fail);
+            });
+
+            waitsFor(function() { return verifier.wasCalled; }, "verifier never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).not.toHaveBeenCalled();
+                expect(verifier).toHaveBeenCalled();
+            });
+        });
+        it("should read empty file properly", function() {
+            // path of file
+            var fileName = "empty.txt",
+                filePath = root.fullPath + '/' + fileName;
+                // file content
+                rule = "",
+                fail = createFail('FileReader'),
+                verifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(evt).toBeDefined();
+                    expect(evt.target.result).toBe(rule);
+                }),
+                // reads file and compares content to what was written
+                read_file = function(evt) {
+                    var reader = new FileReader();
+                    reader.onloadend = verifier;
+                    var myFile = new File();
+                    myFile.fullPath = filePath;
+                    reader.readAsText(myFile);
+                };
+
+            // create a file, write to it, and read it in again
+            runs(function() {
+                root.getFile(fileName, {create: true}, read_file, fail);
+            });
+
+            waitsFor(function() { return verifier.wasCalled; }, "verifier never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).not.toHaveBeenCalled();
+                expect(verifier).toHaveBeenCalled();
+            });
+        });
+        it("should error out on non-existent file", function() {
+            var reader = new FileReader();
+            var verifier = jasmine.createSpy().andCallFake(function(evt) {
+                expect(evt).toBeDefined();
+                expect(evt.target.error).toBeFileError(FileError.NOT_FOUND_ERR);
+            });
+            reader.onerror = verifier;
+            var myFile = new File();
+            myFile.fullPath = root.fullPath + '/' + "doesnotexist.err";
+
+            runs(function() {
+                reader.readAsText(myFile);
+            });
+
+            waitsFor(function() { return verifier.wasCalled; }, "verifier never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(verifier).toHaveBeenCalled();
+            });
+        });
+        it("should read file properly, Data URI", function() {
+            // path of file
+            var fileName = "reader.txt",
+                filePath = root.fullPath + '/' + fileName,
+                fail = createFail('FileReader'),
+                // file content
+                rule = "There is an exception to every rule.  Except this one.",
+                // creates a FileWriter object
+                create_writer = function(fileEntry) {
+                    fileEntry.createWriter(write_file, fail);
+                },
+                // writes file and reads it back in
+                write_file = function(writer) {
+                    writer.onwriteend = read_file;
+                    writer.write(rule);
+                },
+                verifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(evt).toBeDefined();
+                    expect(evt.target.result.substr(0,23)).toBe("data:text/plain;base64,");
+                }),
+                // reads file and compares content to what was written
+                read_file = function(evt) {
+                    var reader = new FileReader();
+                    reader.onloadend = verifier;
+                    var myFile = new File();
+                    myFile.fullPath = filePath;
+                    reader.readAsDataURL(myFile);
+                };
+
+            // create a file, write to it, and read it in again
+            runs(function() {
+                root.getFile(fileName, {create: true}, create_writer, fail);
+            });
+
+            waitsFor(function() { return verifier.wasCalled; }, "verifier never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).not.toHaveBeenCalled();
+                expect(verifier).toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe('FileWriter', function(){
+        it("should have correct methods", function() {
+            // retrieve a FileWriter object
+            var fileName = "writer.methods",
+                fail = createFail('FileWriter'),
+                verifier = jasmine.createSpy().andCallFake(function(writer) {
+                    expect(writer).toBeDefined();
+                    expect(typeof writer.write).toBe('function');
+                    expect(typeof writer.seek).toBe('function');
+                    expect(typeof writer.truncate).toBe('function');
+                    expect(typeof writer.abort).toBe('function');
+
+                    // cleanup
+                    deleteFile(fileName);
+                }),
+                it_writer = function(fileEntry) {
+                    fileEntry.createWriter(verifier, fail);
+                };
+
+            // it FileWriter
+            runs(function() {
+                root.getFile(fileName, {create: true}, it_writer, fail);
+            });
+
+            waitsFor(function() { return verifier.wasCalled; }, "verifier never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).not.toHaveBeenCalled();
+                expect(verifier).toHaveBeenCalled();
+            });
+        });
+        it("should be able to write and append to file, createWriter", function() {
+            var fileName = "writer.append",
+                theWriter,
+                filePath = root.fullPath + '/' + fileName,
+                // file content
+                rule = "There is an exception to every rule.",
+                // for checkin file length
+                length = rule.length,
+                fail = createFail('FileWriter'),
+                verifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(theWriter.length).toBe(length);
+                    expect(theWriter.position).toBe(length);
+
+                    // append some more stuff
+                    var exception = "  Except this one.";
+                    theWriter.onwriteend = anotherVerifier;
+                    length += exception.length;
+                    theWriter.seek(theWriter.length);
+                    theWriter.write(exception);
+                }),
+                anotherVerifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(theWriter.length).toBe(length);
+                    expect(theWriter.position).toBe(length);
+
+                    // cleanup
+                    deleteFile(fileName);
+                }),
+                // writes initial file content
+                write_file = function(fileEntry) {
+                    fileEntry.createWriter(function(writer) {
+                        theWriter = writer;
+                        writer.onwriteend = verifier;
+                        writer.write(rule);
+                    }, fail);
+                };
+
+            // create file, then write and append to it
+            runs(function() {
+                createFile(fileName, write_file);
+            });
+
+            waitsFor(function() { return anotherVerifier.wasCalled; }, "verifier never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).not.toHaveBeenCalled();
+                expect(verifier).toHaveBeenCalled();
+                expect(anotherVerifier).toHaveBeenCalled();
+            });
+        });
+        it("should be able to write and append to file, File object", function() {
+            var fileName = "writer.append",
+                theWriter,
+                filePath = root.fullPath + '/' + fileName,
+                // file content
+                rule = "There is an exception to every rule.",
+                // for checking file length
+                length = rule.length,
+                verifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(theWriter.length).toBe(length);
+                    expect(theWriter.position).toBe(length);
+
+                    // append some more stuff
+                    var exception = "  Except this one.";
+                    theWriter.onwriteend = anotherVerifier;
+                    length += exception.length;
+                    theWriter.seek(theWriter.length);
+                    theWriter.write(exception);
+                }),
+                anotherVerifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(theWriter.length).toBe(length);
+                    expect(theWriter.position).toBe(length);
+
+                    // cleanup
+                    deleteFile(fileName);
+                }),
+                // writes initial file content
+                write_file = function(file) {
+                    theWriter = new FileWriter(file);
+                    theWriter.onwriteend = verifier;
+                    theWriter.write(rule);
+                };
+
+            // create file, then write and append to it
+            runs(function() {
+                var file = new File();
+                file.fullPath = filePath;
+                write_file(file);
+            });
+
+            waitsFor(function() { return anotherVerifier.wasCalled; }, "verifier", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(verifier).toHaveBeenCalled();
+                expect(anotherVerifier).toHaveBeenCalled();
+            });
+        });
+        it("should be able to seek to the middle of the file and write more data than file.length", function() {
+            var fileName = "writer.seek.write",
+                filePath = root.fullPath + '/' + fileName,
+                theWriter,
+                // file content
+                rule = "This is our sentence.",
+                // for iting file length
+                length = rule.length,
+                fail = createFail('FileWriter'),
+                verifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(theWriter.length).toBe(length);
+                    expect(theWriter.position).toBe(length);
+
+                    // append some more stuff
+                    var exception = "newer sentence.";
+                    theWriter.onwriteend = anotherVerifier;
+                    length = 12 + exception.length;
+                    theWriter.seek(12);
+                    theWriter.write(exception);
+                }),
+                anotherVerifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(theWriter.length).toBe(length);
+                    expect(theWriter.position).toBe(length);
+
+                    // cleanup
+                    deleteFile(fileName);
+                }),
+                // writes initial file content
+                write_file = function(fileEntry) {
+                    fileEntry.createWriter(function(writer) {
+                        theWriter = writer;
+                        theWriter.onwriteend = verifier;
+                        theWriter.write(rule);
+                    }, fail);
+                };
+
+            // create file, then write and append to it
+            runs(function() {
+                createFile(fileName, write_file);
+            });
+
+            waitsFor(function() { return anotherVerifier.wasCalled; }, "verifier never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(verifier).toHaveBeenCalled();
+                expect(anotherVerifier).toHaveBeenCalled();
+            });
+        });
+        it("should be able to seek to the middle of the file and write less data than file.length", function() {
+            var fileName = "writer.seek.write2",
+                filePath = root.fullPath + '/' + fileName,
+                // file content
+                rule = "This is our sentence.",
+                theWriter,
+                fail = createFail('FileWriter'),
+                // for iting file length
+                length = rule.length,
+                verifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(theWriter.length).toBe(length);
+                    expect(theWriter.position).toBe(length);
+
+                    // append some more stuff
+                    var exception = "new.";
+                    theWriter.onwriteend = anotherVerifier;
+                    length = 8 + exception.length;
+                    theWriter.seek(8);
+                    theWriter.write(exception);
+                }),
+                anotherVerifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(theWriter.length).toBe(length);
+                    expect(theWriter.position).toBe(length);
+
+                    // cleanup
+                    deleteFile(fileName);
+                }),
+                // writes initial file content
+                write_file = function(fileEntry) {
+                    fileEntry.createWriter(function(writer) {
+                        theWriter = writer;
+                        theWriter.onwriteend = verifier;
+                        theWriter.write(rule);
+                    }, fail);
+                };
+
+            // create file, then write and append to it
+            runs(function() {
+                createFile(fileName, write_file);
+            });
+
+            waitsFor(function() { return anotherVerifier.wasCalled; }, "verifier never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(verifier).toHaveBeenCalled();
+                expect(anotherVerifier).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("should be able to write XML data", function() {
+            var fileName = "writer.xml",
+                filePath = root.fullPath + '/' + fileName,
+                fail = createFail('FileWriter'),
+                theWriter,
+                // file content
+                rule = '<?xml version="1.0" encoding="UTF-8"?>\n<it prop="ack">\nData\n</it>\n',
+                // for iting file length
+                length = rule.length,
+                verifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(theWriter.length).toBe(length);
+                    expect(theWriter.position).toBe(length);
+
+                    // cleanup
+                    deleteFile(fileName);
+                }),
+                // writes file content
+                write_file = function(fileEntry) {
+                    fileEntry.createWriter(function(writer) {
+                        theWriter = writer;
+                        theWriter.onwriteend = verifier;
+                        theWriter.write(rule);
+                    }, fail);
+                };
+
+            // creates file, then write XML data
+            runs(function() {
+                createFile(fileName, write_file);
+            });
+
+            waitsFor(function() { return verifier.wasCalled; }, "verifier", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(verifier).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("should be able to write JSON data", function() {
+            var fileName = "writer.json",
+                filePath = root.fullPath + '/' + fileName,
+                theWriter,
+                // file content
+                rule = '{ "name": "Guy Incognito", "email": "here@there.com" }',
+                fail = createFail('FileWriter'),
+                // for iting file length
+                length = rule.length,
+                verifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(theWriter.length).toBe(length);
+                    expect(theWriter.position).toBe(length);
+
+                    // cleanup
+                    deleteFile(fileName);
+                }),
+                // writes file content
+                write_file = function(fileEntry) {
+                    fileEntry.createWriter(function(writer) {
+                        theWriter = writer;
+                        theWriter.onwriteend = verifier;
+                        theWriter.write(rule);
+                    }, fail);
+                };
+
+            // creates file, then write JSON content
+            runs(function() {
+                createFile(fileName, write_file);
+            });
+
+            waitsFor(function() { return verifier.wasCalled; }, "verifier", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(verifier).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("should write and read special characters", function() {
+            var fileName = "reader.txt",
+                filePath = root.fullPath + '/' + fileName,
+                theWriter,
+                // file content
+                rule = "H\u00EBll\u00F5 Euro \u20AC\u00A1",
+                fail = createFail('FileWriter'),
+                // creates a FileWriter object
+                create_writer = function(fileEntry) {
+                    fileEntry.createWriter(write_file, fail);
+                },
+                verifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(evt).toBeDefined();
+                    expect(evt.target.result).toBe(rule);
+                    // cleanup
+                    deleteFile(fileName);
+                }),
+                // writes file and reads it back in
+                write_file = function(writer) {
+                    theWriter = writer;
+                    theWriter.onwriteend = read_file;
+                    theWriter.write(rule);
+                },
+                // reads file and compares content to what was written
+                read_file = function(evt) {
+                    var reader = new FileReader();
+                    reader.onloadend = verifier;
+                    var myFile = new File();
+                    myFile.fullPath = filePath;
+                    reader.readAsText(myFile);
+                };
+
+            // create a file, write to it, and read it in again
+            runs(function() {
+                createFile(fileName, create_writer, fail);
+            });
+
+            waitsFor(function() { return verifier.wasCalled; }, "verifier never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(verifier).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("should be able to seek", function() {
+            var fileName = "writer.seek",
+                // file content
+                rule = "There is an exception to every rule.  Except this one.",
+                theWriter,
+                // for iting file length
+                length = rule.length,
+                fail = createFail('FileWriter'),
+                verifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(theWriter.position).toBe(length);
+                    theWriter.seek(-5);
+                    expect(theWriter.position).toBe(length-5);
+                    theWriter.seek(length + 100);
+                    expect(theWriter.position).toBe(length);
+                    theWriter.seek(10);
+                    expect(theWriter.position).toBe(10);
+
+                    // cleanup
+                    deleteFile(fileName);
+                }),
+                // writes file content and its writer.seek
+                seek_file = function(fileEntry) {
+                    fileEntry.createWriter(function(writer) {
+                        theWriter = writer;
+                        theWriter.onwriteend = verifier;
+                        theWriter.seek(-100);
+                        expect(theWriter.position).toBe(0);
+                        theWriter.write(rule);
+                    }, fail);
+                };
+
+            // creates file, then write JSON content
+            runs(function() {
+                createFile(fileName, seek_file);
+            });
+
+            waitsFor(function() { return verifier.wasCalled; }, "verifier never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(verifier).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("should be able to truncate", function() {
+            var fileName = "writer.truncate",
+                rule = "There is an exception to every rule.  Except this one.",
+                fail = createFail('FileWRiter'),
+                theWriter,
+                // writes file content
+                write_file = function(fileEntry) {
+                    fileEntry.createWriter(function(writer) {
+                        theWriter = writer;
+                        theWriter.onwriteend = function(evt) {
+                            truncate_file(theWriter);
+                        };
+                        theWriter.write(rule);
+                    }, fail);
+                },
+                verifier = jasmine.createSpy().andCallFake(function(evt) {
+                    expect(theWriter.length).toBe(36);
+                    expect(theWriter.position).toBe(36);
+
+                    // cleanup
+                    deleteFile(fileName);
+                }),
+                // and its writer.truncate
+                truncate_file = function(writer) {
+                    writer.onwriteend = verifier;
+                    writer.truncate(36);
+                };
+
+            // creates file, writes to it, then truncates it
+            runs(function() {
+                createFile(fileName, write_file);
+            });
+
+            waitsFor(function() { return verifier.wasCalled; }, "verifier never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(verifier).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/filetransfer.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/filetransfer.tests.js
new file mode 100644
index 0000000..0779116
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/filetransfer.tests.js
@@ -0,0 +1,407 @@
+describe('FileTransfer', function() {
+
+    // https://github.com/don/cordova-filetransfer
+    var server = "http://cordova-filetransfer.jitsu.com";
+
+    // deletes and re-creates the specified content
+    var writeFile = function(fileName, fileContent, success, error) {
+        var content = fileContent;
+        deleteEntry(fileName, function() {
+            root.getFile(fileName, {create: true}, function(fileEntry) {
+                fileEntry.createWriter(function (writer) {
+
+                    writer.onwrite = function(evt) {
+                        success(fileEntry);
+                    };
+
+                    writer.onabort = function(evt) {
+                        error(evt);
+                    };
+
+                    writer.error = function(evt) {
+                        error(evt);
+                    };
+
+                    writer.write(content + "\n");
+                }, error);
+            }, error);
+        }, error);
+    };
+
+    var getMalformedUrl = function() {
+        if (device.platform.match(/Android/i)) {
+            // bad protocol causes a MalformedUrlException on Android
+            return "httpssss://example.com";
+        } else {
+            // iOS doesn't care about protocol, space in hostname causes error
+            return "httpssss://exa mple.com";
+        }
+    };
+
+    // NOTE: copied from file.tests.js
+    // deletes specified file or directory
+    var deleteEntry = function(name, success, error) {
+        // deletes entry, if it exists
+        window.resolveLocalFileSystemURI(root.toURL() + '/' + name,
+            function(entry) {
+                if (entry.isDirectory === true) {
+                    entry.removeRecursively(success, error);
+                } else {
+                    entry.remove(success, error);
+                }
+            }, success);
+    };
+    // deletes and re-creates the specified file
+    var createFile = function(fileName, success, error) {
+        deleteEntry(fileName, function() {
+            root.getFile(fileName, {create: true}, success, error);
+        }, error);
+    };
+    // deletes file, if it exists, then invokes callback
+    var deleteFile = function(fileName, callback) {
+        root.getFile(fileName, null,
+            // remove file system entry
+            function(entry) {
+                entry.remove(callback, function() { console.log('[ERROR] deleteFile cleanup method invoked fail callback.'); });
+            },
+            // doesn't exist
+            callback);
+    };
+    // end copied from file.tests.js
+
+    it("should exist and be constructable", function() {
+        var ft = new FileTransfer();
+        expect(ft).toBeDefined();
+    });
+    it("should contain proper functions", function() {
+        var ft = new FileTransfer();
+        expect(typeof ft.upload).toBe('function');
+        expect(typeof ft.download).toBe('function');
+    });
+    describe('FileTransferError', function() {
+        it("FileTransferError constants should be defined", function() {
+            expect(FileTransferError.FILE_NOT_FOUND_ERR).toBe(1);
+            expect(FileTransferError.INVALID_URL_ERR).toBe(2);
+            expect(FileTransferError.CONNECTION_ERR).toBe(3);
+        });
+    });
+
+    describe('download method', function() {
+
+        // NOTE: if download tests are failing, check the white list
+        // Android
+        //   <access origin="httpssss://example.com"/>
+        //   <access origin="apache.org" subdomains="true" />
+        //   <access origin="cordova-filetransfer.jitsu.com"/>
+        // iOS
+        //   # Cordova.plist
+        //   ExternalHosts
+        //     - Item 1 String cordova-filetransfer.jitsu.com
+        //     - Item 2 String *.apache.org
+
+        it("should be able to download a file", function() {
+            var fail = jasmine.createSpy();
+            var remoteFile = server + "/robots.txt"
+            var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1);
+            var downloadWin = jasmine.createSpy().andCallFake(function(entry) {
+                expect(entry.name).toBe(localFileName);
+                deleteFile(localFileName);
+            });
+
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, fail);
+            });
+
+            waitsFor(function() { return downloadWin.wasCalled; }, "downloadWin", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(downloadWin).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("should get http status on failure", function() {
+            var downloadWin = jasmine.createSpy();
+
+            var remoteFile = server + "/404";
+            var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1);
+            var downloadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.http_status).toBe(404);
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+                deleteFile(localFileName);
+            });
+
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail);
+            });
+
+            waitsFor(function() { return downloadFail.wasCalled; }, "downloadFail", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(downloadFail).toHaveBeenCalled();
+                expect(downloadWin).not.toHaveBeenCalled();
+            });
+        });
+        it("should handle malformed urls", function() {
+
+            var downloadWin = jasmine.createSpy();
+
+            var remoteFile = getMalformedUrl();
+            var localFileName = "download_malformed_url.txt";
+            var downloadFail = jasmine.createSpy().andCallFake(function(error) {
+
+                // Note: Android needs the bad protocol to be added to the access list
+                // <access origin=".*"/> won't match because ^https?:// is prepended to the regex
+                // The bad protocol must begin with http to avoid automatic prefix
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+                expect(error.code).toBe(FileTransferError.INVALID_URL_ERR);
+                deleteFile(localFileName);
+            });
+
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail);
+            });
+
+            waitsFor(function() { return downloadFail.wasCalled; }, "downloadFail", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(downloadFail).toHaveBeenCalled();
+                expect(downloadWin).not.toHaveBeenCalled();
+            });
+        });
+        it("should handle unknown host", function() {
+            var downloadWin = jasmine.createSpy();
+
+            var remoteFile = "http://foobar.apache.org/index.html";
+            var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1);
+            var downloadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.CONNECTION_ERR);
+            });
+
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.download(remoteFile, root.fullPath + "/" + localFileName, downloadWin, downloadFail);
+            });
+
+            waitsFor(function() { return downloadFail.wasCalled; }, "downloadFail", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(downloadFail).toHaveBeenCalled();
+                expect(downloadWin).not.toHaveBeenCalled();
+            });
+        });
+        it("should handle bad file path", function() {
+            var fail = jasmine.createSpy();
+            var downloadWin = jasmine.createSpy();
+
+            var remoteFile = server;
+            var badFilePath = "c:\\54321";
+            var downloadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR);
+            });
+
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.download(remoteFile, badFilePath, downloadWin, downloadFail);
+            });
+
+            waitsFor(function() { return downloadFail.wasCalled; }, "downloadFail", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(downloadFail).toHaveBeenCalled();
+                expect(downloadWin).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+    });
+    describe('upload method', function() {
+
+        it("should be able to upload a file", function() {
+            var fail = jasmine.createSpy();
+            var uploadFail = jasmine.createSpy();
+
+            var remoteFile = server + "/upload";
+            var localFileName = "upload.txt";
+
+            var uploadWin = jasmine.createSpy().andCallFake(function(uploadResult) {
+                expect(uploadResult.bytesSent).toBeGreaterThan(0);
+                expect(uploadResult.responseCode).toBe(200);
+                expect(uploadResult.response).toBeDefined();
+                deleteEntry(localFileName);
+            });
+
+            var fileWin = function(fileEntry) {
+                ft = new FileTransfer();
+
+                var options = new FileUploadOptions();
+                options.fileKey = "file";
+                options.fileName = localFileName;
+                options.mimeType = "text/plain";
+
+                var params = new Object();
+                params.value1 = "test";
+                params.value2 = "param";
+                options.params = params;
+
+                // removing options cause Android to timeout
+                ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, options);
+            };
+
+            runs(function() {
+                writeFile(localFileName, "this file should upload", fileWin, fail);
+            });
+
+            waitsFor(function() { return uploadWin.wasCalled; }, "uploadWin", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(uploadWin).toHaveBeenCalled();
+                expect(uploadFail).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("should get http status on failure", function() {
+            var fail = jasmine.createSpy();
+            var uploadWin = jasmine.createSpy();
+
+            var remoteFile = server + "/403";
+            var localFileName = "upload_expect_fail.txt";
+            var uploadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.http_status).toBe(403);
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+                deleteEntry(localFileName);
+            });
+
+            var fileWin = function(fileEntry) {
+                var ft = new FileTransfer();
+
+                var options = new FileUploadOptions();
+                options.fileKey="file";
+                options.fileName=fileEntry.name;
+                options.mimeType="text/plain";
+
+                ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, options);
+            };
+
+            runs(function() {
+                writeFile(localFileName, "this file should fail to upload", fileWin, fail);
+            });
+
+            waitsFor(function() { return uploadFail.wasCalled; }, "Expecting file upload to fail", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(uploadFail).toHaveBeenCalled();
+                expect(uploadWin).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("should handle malformed urls", function() {
+            var fail = jasmine.createSpy();
+            var uploadWin = jasmine.createSpy();
+
+            var remoteFile = getMalformedUrl();
+            var localFileName = "malformed_url.txt";
+            var uploadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.INVALID_URL_ERR);
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+                deleteFile(localFileName);
+            });
+            var fileWin = function(fileEntry) {
+                var ft = new FileTransfer();
+                ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, {});
+            };
+
+            runs(function() {
+                writeFile(localFileName, "Some content", fileWin, fail);
+            });
+
+            waitsFor(function() { return uploadFail.wasCalled; }, "uploadFail", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(uploadFail).toHaveBeenCalled();
+                expect(uploadWin).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("should handle unknown host", function() {
+            var fail = jasmine.createSpy();
+            var uploadWin = jasmine.createSpy();
+
+            var remoteFile = "http://foobar.apache.org/robots.txt";
+            var localFileName = remoteFile.substring(remoteFile.lastIndexOf('/')+1);
+            var uploadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.CONNECTION_ERR);
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+                deleteFile(localFileName);
+            });
+            var fileWin = function(fileEntry) {
+                var ft = new FileTransfer();
+                ft.upload(fileEntry.fullPath, remoteFile, uploadWin, uploadFail, {});
+            };
+
+            runs(function() {
+                writeFile(localFileName, "# allow all", fileWin, fail);
+            });
+
+            waitsFor(function() { return uploadFail.wasCalled; }, "uploadFail", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(uploadFail).toHaveBeenCalled();
+                expect(uploadWin).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("should handle missing file", function() {
+            var fail = jasmine.createSpy();
+            var uploadWin = jasmine.createSpy();
+
+            var remoteFile = server + "/upload";
+            var localFileName = "does_not_exist.txt";
+
+            var uploadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR);
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+            });
+
+            runs(function() {
+                deleteFile(localFileName, function() {
+                    var ft = new FileTransfer();
+                    ft.upload(root.fullPath + "/" + localFileName, remoteFile, uploadWin, uploadFail);
+                }, fail);
+            });
+
+            waitsFor(function() { return uploadFail.wasCalled; }, "uploadFail", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(uploadFail).toHaveBeenCalled();
+                expect(uploadWin).not.toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("should handle bad file path", function() {
+            var uploadWin = jasmine.createSpy();
+
+            var remoteFile = server + "/upload";
+
+            var uploadFail = jasmine.createSpy().andCallFake(function(error) {
+                expect(error.code).toBe(FileTransferError.FILE_NOT_FOUND_ERR);
+                expect(error.http_status).not.toBe(401, "Ensure " + remoteFile + " is in the white list");
+            });
+
+            runs(function() {
+                var ft = new FileTransfer();
+                ft.upload("/usr/local/bad/file/path.txt", remoteFile, uploadWin, uploadFail);
+            });
+
+            waitsFor(function() { return uploadFail.wasCalled; }, "uploadFail", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(uploadFail).toHaveBeenCalled();
+                expect(uploadWin).not.toHaveBeenCalled();
+            });
+
+        });
+
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/geolocation.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/geolocation.tests.js
new file mode 100644
index 0000000..685a7d6
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/geolocation.tests.js
@@ -0,0 +1,142 @@
+describe('Geolocation (navigator.geolocation)', function () {
+    it("should exist", function() {
+        expect(navigator.geolocation).toBeDefined();
+    });
+
+    it("should contain a getCurrentPosition function", function() {
+        expect(typeof navigator.geolocation.getCurrentPosition).toBeDefined();
+        expect(typeof navigator.geolocation.getCurrentPosition == 'function').toBe(true);
+    });
+
+    it("should contain a watchPosition function", function() {
+        expect(typeof navigator.geolocation.watchPosition).toBeDefined();
+        expect(typeof navigator.geolocation.watchPosition == 'function').toBe(true);
+    });
+
+    it("should contain a clearWatch function", function() {
+        expect(typeof navigator.geolocation.clearWatch).toBeDefined();
+        expect(typeof navigator.geolocation.clearWatch == 'function').toBe(true);
+    });
+
+    describe('getCurrentPosition method', function() {
+        describe('error callback', function() {
+            it("should be called if we set timeout to 0 and maximumAge to a very small number", function() {
+                console.log("Here I am");
+                var win = jasmine.createSpy(),
+                    fail = jasmine.createSpy();
+
+                runs(function () {
+                    navigator.geolocation.getCurrentPosition(win, fail, {
+                        maximumAge: 0,
+                        timeout: 0
+                    });
+                });
+
+                waitsFor(function () { return fail.wasCalled; }, "fail never called", 250); //small timeout as this should fire very fast
+
+                runs(function () {
+                    expect(win).not.toHaveBeenCalled();
+                });
+            });
+        });
+
+        describe('success callback', function() {
+            it("should be called with a Position object", function() {
+                var win = jasmine.createSpy().andCallFake(function(p) {
+                          expect(p.coords).toBeDefined();
+                          expect(p.timestamp).toBeDefined();
+                          expect(p.timestamp instanceof Date).toBe(true);
+                      }),
+                      fail = jasmine.createSpy();
+
+                runs(function () {
+                    navigator.geolocation.getCurrentPosition(win, fail, {
+                        maximumAge:300000 // 5 minutes maximum age of cached position
+                    });
+                });
+
+                waitsFor(function () { return win.wasCalled; }, "win never called", 20000);
+
+                runs(function () {
+                    expect(fail).not.toHaveBeenCalled();
+                });
+            });
+        });
+    });
+
+    describe('watchPosition method', function() {
+        describe('error callback', function() {
+            var errorWatch = null;
+
+            afterEach(function() {
+                navigator.geolocation.clearWatch(errorWatch);
+            });
+            it("should be called if we set timeout to 0 and maximumAge to a very small number", function() {
+                var win = jasmine.createSpy(),
+                    fail = jasmine.createSpy();
+
+                runs(function () {
+                    errorWatch = navigator.geolocation.watchPosition(win, fail, {
+                        maximumAge: 0,
+                        timeout: 0
+                    });
+                });
+
+                waitsFor(function () { return fail.wasCalled; }, "fail never called", 250); // small timeout as this hsould fire very quickly
+
+                runs(function () {
+                    expect(win).not.toHaveBeenCalled();
+                });
+            });
+        });
+
+        describe('success callback', function() {
+            var successWatch = null;
+
+            afterEach(function() {
+                navigator.geolocation.clearWatch(successWatch);
+            });
+            it("should be called with a Position object", function() {
+                var win = jasmine.createSpy().andCallFake(function(p) {
+                          expect(p.coords).toBeDefined();
+                          expect(p.timestamp).toBeDefined();
+                          expect(p.timestamp instanceof Date).toBe(true);
+                      }),
+                      fail = jasmine.createSpy();
+
+                runs(function () {
+                    successWatch = navigator.geolocation.watchPosition(win, fail, {
+                        maximumAge:300000 // 5 minutes maximum age of cached position
+                    });
+                });
+
+                waitsFor(function () { return win.wasCalled; }, "win never called", 20000);
+
+                runs(function () {
+                    expect(fail).not.toHaveBeenCalled();
+                });
+            });
+        });
+    });
+
+    describe("Geolocation model", function () {
+        it("should be able to define a Position object with coords and timestamp properties", function() {
+            var pos = new Position({}, new Date());
+            expect(pos).toBeDefined();
+            expect(pos.coords).toBeDefined();
+            expect(pos.timestamp).toBeDefined();
+        });
+
+        it("should be able to define a Coordinates object with latitude, longitude, accuracy, altitude, heading, speed and altitudeAccuracy properties", function() {
+            var coords = new Coordinates(1,2,3,4,5,6,7);
+            expect(coords).toBeDefined();
+            expect(coords.latitude).toBeDefined();
+            expect(coords.longitude).toBeDefined();
+            expect(coords.accuracy).toBeDefined();
+            expect(coords.altitude).toBeDefined();
+            expect(coords.heading).toBeDefined();
+            expect(coords.speed).toBeDefined();
+            expect(coords.altitudeAccuracy).toBeDefined();
+        });
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/media.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/media.tests.js
new file mode 100644
index 0000000..d0e6c4f
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/media.tests.js
@@ -0,0 +1,144 @@
+describe('Media', function () {
+	it("should exist", function() {
+        expect(Media).toBeDefined();
+		expect(typeof Media).toBe("function");
+	});
+
+    it("should have the following properties", function() {
+        var media1 = new Media("dummy");
+        expect(media1.id).toBeDefined();
+        expect(media1.src).toBeDefined();
+        expect(media1._duration).toBeDefined();
+        expect(media1._position).toBeDefined();
+        media1.release();
+    });
+
+	it("should define constants for Media errors", function() {
+        expect(MediaError).toBeDefined();
+        expect(MediaError.MEDIA_ERR_NONE_ACTIVE).toBe(0);
+        expect(MediaError.MEDIA_ERR_ABORTED).toBe(1);
+		expect(MediaError.MEDIA_ERR_NETWORK).toBe(2);
+		expect(MediaError.MEDIA_ERR_DECODE).toBe(3);
+		expect(MediaError.MEDIA_ERR_NONE_SUPPORTED).toBe(4);
+	});
+
+    it("should contain a play function", function() {
+        var media1 = new Media();
+        expect(media1.play).toBeDefined();
+        expect(typeof media1.play).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a stop function", function() {
+        var media1 = new Media();
+        expect(media1.stop).toBeDefined();
+        expect(typeof media1.stop).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a seekTo function", function() {
+        var media1 = new Media();
+        expect(media1.seekTo).toBeDefined();
+        expect(typeof media1.seekTo).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a pause function", function() {
+        var media1 = new Media();
+        expect(media1.pause).toBeDefined();
+        expect(typeof media1.pause).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a getDuration function", function() {
+        var media1 = new Media();
+        expect(media1.getDuration).toBeDefined();
+        expect(typeof media1.getDuration).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a getCurrentPosition function", function() {
+        var media1 = new Media();
+        expect(media1.getCurrentPosition).toBeDefined();
+        expect(typeof media1.getCurrentPosition).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a startRecord function", function() {
+        var media1 = new Media();
+        expect(media1.startRecord).toBeDefined();
+        expect(typeof media1.startRecord).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a stopRecord function", function() {
+        var media1 = new Media();
+        expect(media1.stopRecord).toBeDefined();
+        expect(typeof media1.stopRecord).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a release function", function() {
+        var media1 = new Media();
+        expect(media1.release).toBeDefined();
+        expect(typeof media1.release).toBe('function');
+        media1.release();
+    });
+
+    it("should contain a setVolume function", function() {
+        var media1 = new Media();
+        expect(media1.setVolume).toBeDefined();
+        expect(typeof media1.setVolume).toBe('function');
+        media1.release();
+    });
+
+	it("should return MediaError for bad filename", function() {
+		var badMedia = null,
+            win = jasmine.createSpy(),
+            fail = jasmine.createSpy().andCallFake(function (result) {
+                expect(result).toBeDefined();
+                expect(result.code).toBe(MediaError.MEDIA_ERR_ABORTED);
+            });
+            
+        runs(function () {
+            badMedia = new Media("invalid.file.name", win,fail);
+            badMedia.play();
+        });
+
+        waitsFor(function () { return fail.wasCalled; }, Tests.TEST_TIMEOUT);
+
+        runs(function () {
+            expect(win).not.toHaveBeenCalled();
+            badMedia.release();
+        });
+	});
+
+    it("position should be set properly", function() {
+        var media1 = new Media("http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3"),
+            test = jasmine.createSpy().andCallFake(function(position) {
+                    console.log("position = " + position);
+                    expect(position).toBeGreaterThan(0.0);
+                    media1.stop()
+                    media1.release();
+                });
+
+        media1.play();
+
+        waits(5000);
+
+        runs(function () {
+            media1.getCurrentPosition(test, function () {});
+        });
+
+        waitsFor(function () { return test.wasCalled; }, Tests.TEST_TIMEOUT);
+    });
+
+    it("duration should be set properly", function() {
+        var media1 = new Media("http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3");
+        media1.play();
+        waits(5000);
+        runs(function () {
+            expect(media1.getDuration()).toBeGreaterThan(0.0);
+        });
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/network.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/network.tests.js
new file mode 100644
index 0000000..780097f
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/network.tests.js
@@ -0,0 +1,25 @@
+describe('Network (navigator.network)', function () {
+	it("should exist", function() {
+        expect(navigator.network).toBeDefined();
+	});
+
+    describe('Network Information API', function () {
+        it("connection should exist", function() {
+            expect(navigator.network.connection).toBeDefined();
+        });
+
+        it("should contain connection properties", function() {
+            expect(navigator.network.connection.type).toBeDefined();
+        });
+
+        it("should define constants for connection status", function() {
+            expect(Connection.UNKNOWN).toBe("unknown");
+            expect(Connection.ETHERNET).toBe("ethernet");
+            expect(Connection.WIFI).toBe("wifi");
+            expect(Connection.CELL_2G).toBe("2g");
+            expect(Connection.CELL_3G).toBe("3g");
+            expect(Connection.CELL_4G).toBe("4g");
+            expect(Connection.NONE).toBe("none");
+        });
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/notification.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/notification.tests.js
new file mode 100644
index 0000000..61c795d
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/notification.tests.js
@@ -0,0 +1,20 @@
+describe('Notification (navigator.notification)', function () {
+	it("should exist", function() {
+        expect(navigator.notification).toBeDefined();
+	});
+
+	it("should contain a vibrate function", function() {
+		expect(typeof navigator.notification.vibrate).toBeDefined();
+		expect(typeof navigator.notification.vibrate).toBe("function");
+	});
+
+	it("should contain a beep function", function() {
+		expect(typeof navigator.notification.beep).toBeDefined();
+		expect(typeof navigator.notification.beep).toBe("function");
+	});
+
+	it("should contain a alert function", function() {
+		expect(typeof navigator.notification.alert).toBeDefined();
+		expect(typeof navigator.notification.alert).toBe("function");
+	});
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/platform.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/platform.tests.js
new file mode 100644
index 0000000..f44fcb3
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/platform.tests.js
@@ -0,0 +1,10 @@
+describe('Platform (cordova)', function () {
+    it("should exist", function() {
+        expect(cordova).toBeDefined();
+    });
+
+    it("exec method should exist", function() {
+        expect(cordova.exec).toBeDefined();
+        expect(typeof cordova.exec).toBe('function');
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/autotest/tests/storage.tests.js b/tizen SDK samples/mobile-spec/autotest/tests/storage.tests.js
new file mode 100644
index 0000000..a921de3
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/autotest/tests/storage.tests.js
@@ -0,0 +1,161 @@
+describe("Session Storage", function () {
+    it("should exist", function () {
+        expect(window.sessionStorage).toBeDefined();
+        expect(typeof window.sessionStorage.length).not.toBe('undefined');
+        expect(typeof(window.sessionStorage.key)).toBe('function');
+        expect(typeof(window.sessionStorage.getItem)).toBe('function');
+        expect(typeof(window.sessionStorage.setItem)).toBe('function');
+        expect(typeof(window.sessionStorage.removeItem)).toBe('function');
+        expect(typeof(window.sessionStorage.clear)).toBe('function');
+    });
+
+    it("check length", function () {
+        expect(window.sessionStorage.length).toBe(0);
+        window.sessionStorage.setItem("key","value");
+        expect(window.sessionStorage.length).toBe(1);
+        window.sessionStorage.removeItem("key");   
+        expect(window.sessionStorage.length).toBe(0);
+    });
+
+    it("check key", function () {
+        expect(window.sessionStorage.key(0)).toBe(null);
+        window.sessionStorage.setItem("test","value");
+        expect(window.sessionStorage.key(0)).toBe("test");
+        window.sessionStorage.removeItem("test");   
+        expect(window.sessionStorage.key(0)).toBe(null);
+    });
+
+    it("check getItem", function() {
+        expect(window.sessionStorage.getItem("item")).toBe(null);
+        window.sessionStorage.setItem("item","value");
+        expect(window.sessionStorage.getItem("item")).toBe("value");
+        window.sessionStorage.removeItem("item");   
+        expect(window.sessionStorage.getItem("item")).toBe(null);
+    });
+
+    it("check setItem", function() {
+        expect(window.sessionStorage.getItem("item")).toBe(null);
+        window.sessionStorage.setItem("item","value");
+        expect(window.sessionStorage.getItem("item")).toBe("value");
+        window.sessionStorage.setItem("item","newval");
+        expect(window.sessionStorage.getItem("item")).toBe("newval");
+        window.sessionStorage.removeItem("item");   
+        expect(window.sessionStorage.getItem("item")).toBe(null);
+    });
+
+    it("can remove an item", function () {
+        expect(window.sessionStorage.getItem("item")).toBe(null);
+        window.sessionStorage.setItem("item","value");
+        expect(window.sessionStorage.getItem("item")).toBe("value");
+        window.sessionStorage.removeItem("item");   
+        expect(window.sessionStorage.getItem("item")).toBe(null);
+    });
+
+    it("check clear", function() {
+        window.sessionStorage.setItem("item1","value");
+        window.sessionStorage.setItem("item2","value");
+        window.sessionStorage.setItem("item3","value");
+        expect(window.sessionStorage.length).toBe(3);
+        window.sessionStorage.clear();
+        expect(window.sessionStorage.length).toBe(0);
+    });
+
+    it("check dot notation", function() {
+        expect(window.sessionStorage.item).not.toBeDefined();
+        window.sessionStorage.item = "value";
+        expect(window.sessionStorage.item).toBe("value");
+        window.sessionStorage.removeItem("item");   
+        expect(window.sessionStorage.item).not.toBeDefined();
+    });
+
+    describe("Local Storage", function () {
+        it("should exist", function() {
+            expect(window.localStorage).toBeDefined();
+            expect(window.localStorage.length).toBeDefined();
+            expect(typeof window.localStorage.key).toBe("function");
+            expect(typeof window.localStorage.getItem).toBe("function");
+            expect(typeof window.localStorage.setItem).toBe("function");
+            expect(typeof window.localStorage.removeItem).toBe("function");
+            expect(typeof window.localStorage.clear).toBe("function");
+        });  
+
+        it("check length", function() {
+            expect(window.localStorage.length).toBe(0);
+            window.localStorage.setItem("key","value");
+            expect(window.localStorage.length).toBe(1);
+            window.localStorage.removeItem("key");   
+            expect(window.localStorage.length).toBe(0);
+        });
+
+        it("check key", function() {
+            expect(window.localStorage.key(0)).toBe(null);
+            window.localStorage.setItem("test","value");
+            expect(window.localStorage.key(0)).toBe("test");
+            window.localStorage.removeItem("test");   
+            expect(window.localStorage.key(0)).toBe(null);
+        });
+
+        it("check getItem", function() {
+            expect(window.localStorage.getItem("item")).toBe(null);
+            window.localStorage.setItem("item","value");
+            expect(window.localStorage.getItem("item")).toBe("value");
+            window.localStorage.removeItem("item");   
+            expect(window.localStorage.getItem("item")).toBe(null);
+        });
+
+        it("check setItem", function() {
+            expect(window.localStorage.getItem("item")).toBe(null);
+            window.localStorage.setItem("item","value");
+            expect(window.localStorage.getItem("item")).toBe("value");
+            window.localStorage.setItem("item","newval");
+            expect(window.localStorage.getItem("item")).toBe("newval");
+            window.localStorage.removeItem("item");   
+            expect(window.localStorage.getItem("item")).toBe(null);
+        });
+
+        it("check removeItem", function() {
+            expect(window.localStorage.getItem("item")).toBe(null);
+            window.localStorage.setItem("item","value");
+            expect(window.localStorage.getItem("item")).toBe("value");
+            window.localStorage.removeItem("item");   
+            expect(window.localStorage.getItem("item")).toBe(null);
+        });
+
+        it("check clear", function() {
+            expect(window.localStorage.getItem("item1")).toBe(null);
+            expect(window.localStorage.getItem("item2")).toBe(null);
+            expect(window.localStorage.getItem("item3")).toBe(null);
+            window.localStorage.setItem("item1","value");
+            window.localStorage.setItem("item2","value");
+            window.localStorage.setItem("item3","value");
+            expect(window.localStorage.getItem("item1")).toBe("value");
+            expect(window.localStorage.getItem("item2")).toBe("value");
+            expect(window.localStorage.getItem("item3")).toBe("value");
+            expect(window.localStorage.length).toBe(3);
+            window.localStorage.clear();
+            expect(window.localStorage.length).toBe(0);
+            expect(window.localStorage.getItem("item1")).toBe(null);
+            expect(window.localStorage.getItem("item2")).toBe(null);
+            expect(window.localStorage.getItem("item3")).toBe(null);
+        });
+
+        it("check dot notation", function() {
+            expect(window.localStorage.item).not.toBeDefined();
+            window.localStorage.item = "value";
+            expect(window.localStorage.item).toBe("value");
+            window.localStorage.removeItem("item");   
+            expect(window.localStorage.item).not.toBeDefined();
+        });
+    });
+
+    describe("HTML 5 Storage", function () {
+        it("should exist", function() {
+            expect(window.openDatabase);
+        });
+
+        it("Should open a database", function() {
+            var db = openDatabase("Database", "1.0", "HTML5 Database API example", 200000);
+            expect(db).toBeDefined();
+        });
+    });
+});
diff --git a/tizen SDK samples/mobile-spec/battery/index.html b/tizen SDK samples/mobile-spec/battery/index.html
new file mode 100644
index 0000000..394988c
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/battery/index.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+            if (!deviceReady) {
+                alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+            }
+        },1000);
+    }
+
+    /* Battery */
+    function updateInfo(info) {
+        document.getElementById('level').innerText = info.level;
+        document.getElementById('isPlugged').innerText = info.isPlugged;
+        if (info.level > 5) {
+            document.getElementById('crit').innerText = "false";
+        }
+        if (info.level > 20) {
+            document.getElementById('low').innerText = "false";
+        }
+    }
+    
+    function batteryLow(info) {
+        document.getElementById('low').innerText = "true";
+    }
+    
+    function batteryCritical(info) {
+        document.getElementById('crit').innerText = "true";
+    }
+    
+    function addBattery() {
+        window.addEventListener("batterystatus", updateInfo, false);
+    }
+    
+    function removeBattery() {
+        window.removeEventListener("batterystatus", updateInfo, false);
+    }
+    
+    function addLow() {
+        window.addEventListener("batterylow", batteryLow, false);
+    }
+    
+    function removeLow() {
+        window.removeEventListener("batterylow", batteryLow, false);
+    }
+    
+    function addCritical() {
+        window.addEventListener("batterycritical", batteryCritical, false);
+    }
+    
+    function removeCritical() {
+        window.removeEventListener("batterycritical", batteryCritical, false);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Battery</h1>
+    <div id="info">
+        <b>Status:</b> <span id="battery_status"></span><br>
+        Level: <span id="level"></span><br/>
+        Plugged: <span id="isPlugged"></span><br/>
+        Low: <span id="low"></span><br/>
+        Critical: <span id="crit"></span><br/>
+    </div>
+    <h2>Action</h2>
+    <div class="btn large" onclick="addBattery();">Add "batterystatus" listener</div>
+    <div class="btn large" onclick="removeBattery();">Remove "batterystatus" listener</div>
+    <div class="btn large" onclick="addLow();">Add "batterylow" listener</div>
+    <div class="btn large" onclick="removeLow();">Remove "batterylow" listener</div>
+    <div class="btn large" onclick="addCritical();">Add "batterycritical" listener</div>
+    <div class="btn large" onclick="removeCritical();">Remove "batterycritical" listener</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>      
diff --git a/tizen SDK samples/mobile-spec/camera/index.html b/tizen SDK samples/mobile-spec/camera/index.html
new file mode 100755
index 0000000..bc3b554
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/camera/index.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    //-------------------------------------------------------------------------
+    // Camera 
+    //-------------------------------------------------------------------------
+
+    /**
+     * Capture picture
+     */
+    function getPicture() {
+        
+        //navigator.camera.getPicture(onPhotoDataSuccess, onFail, { quality: 50, 
+        //    destinationType: Camera.DestinationType.FILE_URI, sourceType : Camera.PictureSourceType.CAMERA });
+        
+        navigator.camera.getPicture(
+            function(data) {
+                var img = document.getElementById('camera_image');
+                img.style.visibility = "visible";
+                img.style.display = "block";
+                //img.src = "data:image/jpeg;base64," + data;
+                img.src = data;
+                document.getElementById('camera_status').innerHTML = "Success";
+            },
+            function(e) {
+                console.log("Error getting picture: " + e);
+                document.getElementById('camera_status').innerHTML = "Error getting picture.";
+            },
+            { quality: 50, destinationType:
+            Camera.DestinationType.FILE_URI, sourceType : Camera.PictureSourceType.CAMERA});
+    };
+
+    /**
+     * Select image from library
+     */
+    function getImage() {
+        navigator.camera.getPicture(
+            function(data) {
+                var img = document.getElementById('camera_image');
+                img.style.visibility = "visible";
+                img.style.display = "block";
+                //img.src = "data:image/jpeg;base64," + data;
+                img.src = data;
+                document.getElementById('camera_status').innerHTML = "Success";
+            },
+            function(e) {
+                console.log("Error getting picture: " + e);
+                document.getElementById('camera_status').innerHTML = "Error getting picture.";
+            },
+            { quality: 50, destinationType:
+            Camera.DestinationType.FILE_URI, sourceType: Camera.PictureSourceType.PHOTOLIBRARY});
+    };
+
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Camera</h1>
+    <div id="info">
+        <b>Status:</b> <span id="camera_status"></span><br>
+        <img style="width:120px;height:120px;visibility:hidden;display:none;" id="camera_image" src="" />
+    </div>
+    <h2>Action</h2>
+    <div class="btn large" onclick="getPicture();">Take Picture</div>
+    <div class="btn large" onclick="getImage();">Select Image from Library</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>      
diff --git a/tizen SDK samples/mobile-spec/compass/index.html b/tizen SDK samples/mobile-spec/compass/index.html
new file mode 100755
index 0000000..8dbf99a
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/compass/index.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    function roundNumber(num) {
+        var dec = 3;
+        var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
+        return result;
+    }
+
+    //-------------------------------------------------------------------------
+    // Compass
+    //-------------------------------------------------------------------------
+    var watchCompassId = null;
+
+    /**
+     * Start watching compass
+     */
+    var watchCompass = function() {
+        console.log("watchCompass()");
+
+        // Success callback
+        var success = function(a){
+            document.getElementById('compassHeading').innerHTML = roundNumber(a.magneticHeading);
+        };
+
+        // Fail callback
+        var fail = function(e){
+            console.log("watchCompass fail callback with error code "+e);
+            stopCompass();
+            setCompassStatus(e);
+        };
+
+        // Update heading every 1 sec
+        var opt = {};
+        opt.frequency = 1000;
+        watchCompassId = navigator.compass.watchHeading(success, fail, opt);
+
+        setCompassStatus("Running");
+    };
+
+    /**
+     * Stop watching the acceleration
+     */
+    var stopCompass = function() {
+        setCompassStatus("Stopped");
+        if (watchCompassId) {
+            navigator.compass.clearWatch(watchCompassId);
+            watchCompassId = null;
+        }
+    };
+
+    /**
+     * Get current compass
+     */
+    var getCompass = function() {
+        console.log("getCompass()");
+
+        // Stop compass if running
+        stopCompass();
+
+        // Success callback
+        var success = function(a){
+            document.getElementById('compassHeading').innerHTML = roundNumber(a.magneticHeading);
+        };
+
+        // Fail callback
+        var fail = function(e){
+            console.log("getCompass fail callback with error code "+e);
+            setCompassStatus(e);
+        };
+
+        // Make call
+        var opt = {};
+        navigator.compass.getCurrentHeading(success, fail, opt);
+    };
+
+    /**
+     * Set compass status
+     */
+    var setCompassStatus = function(status) {
+        document.getElementById('compass_status').innerHTML = status;
+    };
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Compass</h1>
+    <div id="info">
+        <b>Status:</b> <span id="compass_status">Stopped</span>
+        <table width="100%"><tr>
+            <td width="33%">Heading: <span id="compassHeading"> </span></td>
+        </tr></table>
+    </div>
+    <h2>Action</h2>
+    <div class="btn large" onclick="getCompass();">Get Compass</div>
+    <div class="btn large" onclick="watchCompass();">Start Watching Compass</div>
+    <div class="btn large" onclick="stopCompass();">Stop Watching Compass</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>      
diff --git a/tizen SDK samples/mobile-spec/config.xml b/tizen SDK samples/mobile-spec/config.xml
new file mode 100644
index 0000000..d6afff6
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/config.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<widget xmlns="http://www.w3.org/ns/widgets" xmlns:tizen="http://tizen.org/ns/widgets" version="2.0 Beta" viewmodes="fullscreen" id="http://yourdomain/mobile-spec">  
+	<icon src="apple-touch-icon.png"/>  
+	<content src="index.html"/>  
+	<name>mobile-spec</name>
+		<feature name="http://tizen.org/api/alarm" required="true"/>  
+	<feature name="http://tizen.org/api/alarm.read" required="true"/>  
+	<feature name="http://tizen.org/api/alarm.write" required="true"/>  
+	<feature name="http://tizen.org/api/application" required="true"/>  
+	<feature name="http://tizen.org/api/application.kill" required="true"/>  
+	<feature name="http://tizen.org/api/application.launch" required="true"/>  
+	<feature name="http://tizen.org/api/application.read" required="true"/>  
+	<feature name="http://tizen.org/api/bluetooth" required="true"/>  
+	<feature name="http://tizen.org/api/bluetooth.admin" required="true"/>  
+	<feature name="http://tizen.org/api/bluetooth.gap" required="true"/>  
+	<feature name="http://tizen.org/api/bluetooth.spp" required="true"/>  
+	<feature name="http://tizen.org/api/calendar" required="true"/>  
+	<feature name="http://tizen.org/api/calendar.read" required="true"/>  
+	<feature name="http://tizen.org/api/calendar.write" required="true"/>  
+	<feature name="http://tizen.org/api/call" required="true"/>  
+	<feature name="http://tizen.org/api/call.history" required="true"/>  
+	<feature name="http://tizen.org/api/call.history.read" required="true"/>  
+	<feature name="http://tizen.org/api/call.history.write" required="true"/>  
+	<feature name="http://tizen.org/api/call.state" required="true"/>  
+	<feature name="http://tizen.org/api/contact" required="true"/>  
+	<feature name="http://tizen.org/api/contact.read" required="true"/>  
+	<feature name="http://tizen.org/api/contact.write" required="true"/>  
+	<feature name="http://tizen.org/api/filesystem" required="true"/>  
+	<feature name="http://tizen.org/api/filesystem.read" required="true"/>  
+	<feature name="http://tizen.org/api/filesystem.write" required="true"/>  
+	<feature name="http://tizen.org/api/geocoder" required="true"/>  
+	<feature name="http://tizen.org/api/lbs" required="true"/>  
+	<feature name="http://tizen.org/api/mediacontent" required="true"/>  
+	<feature name="http://tizen.org/api/mediacontent.read" required="true"/>  
+	<feature name="http://tizen.org/api/messaging" required="true"/>  
+	<feature name="http://tizen.org/api/messaging.read" required="true"/>  
+	<feature name="http://tizen.org/api/messaging.send" required="true"/>  
+	<feature name="http://tizen.org/api/messaging.write" required="true"/>  
+	<feature name="http://tizen.org/api/nfc" required="true"/>  
+	<feature name="http://tizen.org/api/nfc.p2p" required="true"/>  
+	<feature name="http://tizen.org/api/nfc.tag" required="true"/>  
+	<feature name="http://tizen.org/api/systeminfo" required="true"/>  
+	<feature name="http://tizen.org/api/time" required="true"/>  
+	<feature name="http://tizen.org/api/time.read" required="true"/>  
+	<feature name="http://tizen.org/api/time.write" required="true"/>  
+	<feature name="http://tizen.org/api/tizen" required="true"/>  
+</widget>
diff --git a/tizen SDK samples/mobile-spec/contacts/index.html b/tizen SDK samples/mobile-spec/contacts/index.html
new file mode 100755
index 0000000..950e1cc
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/contacts/index.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,maximum-scale=1.0,initial-scale=1.0" />
+    <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <!-- ISO-8859-1 -->
+    <title>Cordova Mobile Spec</title>
+    <link rel="stylesheet" href="../master.css" type="text/css" media="screen" title="no title" charset="utf-8">
+    <script type="text/javascript" charset="utf-8" src="../cordova.js"></script>      
+
+      
+<script type="text/javascript" charset="utf-8">
+
+    var deviceReady = false;
+
+    //-------------------------------------------------------------------------
+    // Contacts
+    //-------------------------------------------------------------------------
+    function getContacts() {
+        obj = new ContactFindOptions();
+        obj.filter = "D"; //Brooks";
+        obj.multiple = true;
+        navigator.contacts.find(
+            ["displayName", "name", "phoneNumbers", "emails", "urls", "note"],
+            function(contacts) {
+                var s = "";
+                if (contacts.length == 0) {
+                    s = "No contacts found";
+                }
+                else {
+                    s = "Number of contacts: "+contacts.length+"<br><table width='100%'><tr><th>Name</th><td>Phone</td><td>Email</td></tr>";
+                    for (var i=0; i<contacts.length; i++) {
+                        var contact = contacts[i];
+                        s = s + "<tr><td>" + contact.name.formatted + "</td><td>";
+                        if (contact.phoneNumbers && contact.phoneNumbers.length > 0) {
+                            s = s + contact.phoneNumbers[0].value;
+                        }
+                        s = s + "</td><td>"
+                        if (contact.emails && contact.emails.length > 0) {
+                            s = s + contact.emails[0].value;
+                        }
+                        s = s + "</td></tr>";
+                    }
+                    s = s + "</table>";
+                }
+                document.getElementById('contacts_results').innerHTML = s;
+            },
+            function(e) {
+                document.getElementById('contacts_results').innerHTML = "Error: "+e.code;
+            },
+            obj);
+    };
+
+    function addContact(){
+        console.log("addContact()");
+        try{
+            var contact = navigator.contacts.create({"displayName": "Dooney Evans"});
+            var contactName = {
+                formatted: "Dooney Evans",
+                familyName: "Evans",
+                givenName: "Dooney",
+                middleName: ""
+            };
+
+            contact.name = contactName;
+
+            var phoneNumbers = [1];
+            phoneNumbers[0] = new ContactField('work', '512-555-1234', true);
+            contact.phoneNumbers = phoneNumbers;
+
+            contact.save(
+                function() { alert("Contact saved.");},
+                function(e) { alert("Contact save failed: " + e.code); }
+            );
+            console.log("you have saved the contact");
+        }
+        catch (e){
+            alert(e);
+        }
+
+    };
+    
+    /**
+     * Function called when page has finished loading.
+     */
+    function init() {
+        document.addEventListener("deviceready", function() {
+                deviceReady = true;
+                console.log("Device="+device.platform+" "+device.version);
+            }, false);
+        window.setTimeout(function() {
+        	if (!deviceReady) {
+        		alert("Error: PhoneGap did not initialize.  Demo will not run correctly.");
+        	}
+        },1000);
+    }
+
+</script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+  
+    <h1>Contacts</h1>    
+    <div id="info">
+        <b>Results:</b><br>
+        <span id="contacts_results"> </span>
+    </div>
+    <h2>Action</h2>
+    <div class="btn large" onclick="getContacts();">Get phone's contacts</div>
+    <div class="btn large" onclick="addContact();">Add a new contact 'Dooney Evans'</div>
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>      
diff --git a/tizen SDK samples/mobile-spec/cordova-2.0.0.js b/tizen SDK samples/mobile-spec/cordova-2.0.0.js
new file mode 100644
index 0000000..98110c7
--- /dev/null
+++ b/tizen SDK samples/mobile-spec/cordova-2.0.0.js
@@ -0,0 +1,7066 @@
+// commit 7dd17b00544742d14ecdeff2148a66480680f12b
+
+// File generated at :: Thu Jul 26 2012 17:32:56 GMT+0200 (CEST)
+
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+ 
+     http://www.apache.org/licenses/LICENSE-2.0
+ 
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/
+
+;(function() {
+
+// file: lib/scripts/require.js
+var require,
+    define;
+
+(function () {
+    var modules = {};
+
+    function build(module) {
+        var factory = module.factory;
+        module.exports = {};
+        delete module.factory;
+        factory(require, module.exports, module);
+        return module.exports;
+    }
+
+    require = function (id) {
+        if (!modules[id]) {
+            throw "module " + id + " not found";
+        }
+        return modules[id].factory ? build(modules[id]) : modules[id].exports;
+    };
+
+    define = function (id, factory) {
+        if (modules[id]) {
+            throw "module " + id + " already defined";
+        }
+
+        modules[id] = {
+            id: id,
+            factory: factory
+        };
+    };
+
+    define.remove = function (id) {
+        delete modules[id];
+    };
+
+})();
+
+//Export for use in node
+if (typeof module === "object" && typeof require === "function") {
+    module.exports.require = require;
+    module.exports.define = define;
+}
+// file: lib/cordova.js
+define("cordova", function(require, exports, module) {
+var channel = require('cordova/channel');
+
+/**
+ * Listen for DOMContentLoaded and notify our channel subscribers.
+ */
+document.addEventListener('DOMContentLoaded', function() {
+    channel.onDOMContentLoaded.fire();
+}, false);
+if (document.readyState == 'complete' || document.readyState == 'interactive') {
+    channel.onDOMContentLoaded.fire();
+}
+
+/**
+ * Intercept calls to addEventListener + removeEventListener and handle deviceready,
+ * resume, and pause events.
+ */
+var m_document_addEventListener = document.addEventListener;
+var m_document_removeEventListener = document.removeEventListener;
+var m_window_addEventListener = window.addEventListener;
+var m_window_removeEventListener = window.removeEventListener;
+
+/**
+ * Houses custom event handlers to intercept on document + window event listeners.
+ */
+var documentEventHandlers = {},
+    windowEventHandlers = {};
+
+document.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    if (typeof documentEventHandlers[e] != 'undefined') {
+        if (evt === 'deviceready') {
+            documentEventHandlers[e].subscribeOnce(handler);
+        } else {
+            documentEventHandlers[e].subscribe(handler);
+        }
+    } else {
+        m_document_addEventListener.call(document, evt, handler, capture);
+    }
+};
+
+window.addEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    if (typeof windowEventHandlers[e] != 'undefined') {
+        windowEventHandlers[e].subscribe(handler);
+    } else {
+        m_window_addEventListener.call(window, evt, handler, capture);
+    }
+};
+
+document.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    // If unsubcribing from an event that is handled by a plugin
+    if (typeof documentEventHandlers[e] != "undefined") {
+        documentEventHandlers[e].unsubscribe(handler);
+    } else {
+        m_document_removeEventListener.call(document, evt, handler, capture);
+    }
+};
+
+window.removeEventListener = function(evt, handler, capture) {
+    var e = evt.toLowerCase();
+    // If unsubcribing from an event that is handled by a plugin
+    if (typeof windowEventHandlers[e] != "undefined") {
+        windowEventHandlers[e].unsubscribe(handler);
+    } else {
+        m_window_removeEventListener.call(window, evt, handler, capture);
+    }
+};
+
+function createEvent(type, data) {
+    var event = document.createEvent('Events');
+    event.initEvent(type, false, false);
+    if (data) {
+        for (var i in data) {
+            if (data.hasOwnProperty(i)) {
+                event[i] = data[i];
+            }
+        }
+    }
+    return event;
+}
+
+if(typeof window.console === "undefined") {
+    window.console = {
+        log:function(){}
+    };
+}
+
+var cordova = {
+    define:define,
+    require:require,
+    /**
+     * Methods to add/remove your own addEventListener hijacking on document + window.
+     */
+    addWindowEventHandler:function(event, opts) {
+        return (windowEventHandlers[event] = channel.create(event, opts));
+    },
+    addDocumentEventHandler:function(event, opts) {
+        return (documentEventHandlers[event] = channel.create(event, opts));
+    },
+    removeWindowEventHandler:function(event) {
+        delete windowEventHandlers[event];
+    },
+    removeDocumentEventHandler:function(event) {
+        delete documentEventHandlers[event];
+    },
+    /**
+     * Retreive original event handlers that were replaced by Cordova
+     *
+     * @return object
+     */
+    getOriginalHandlers: function() {
+        return {'document': {'addEventListener': m_document_addEventListener, 'removeEventListener': m_document_removeEventListener},
+        'window': {'addEventListener': m_window_addEventListener, 'removeEventListener': m_window_removeEventListener}};
+    },
+    /**
+     * Method to fire event from native code
+     */
+    fireDocumentEvent: function(type, data) {
+        var evt = createEvent(type, data);
+        if (typeof documentEventHandlers[type] != 'undefined') {
+            setTimeout(function() {
+                documentEventHandlers[type].fire(evt);
+            }, 0);
+        } else {
+            document.dispatchEvent(evt);
+        }
+    },
+    fireWindowEvent: function(type, data) {
+        var evt = createEvent(type,data);
+        if (typeof windowEventHandlers[type] != 'undefined') {
+            setTimeout(function() {
+                windowEventHandlers[type].fire(evt);
+            }, 0);
+        } else {
+            window.dispatchEvent(evt);
+        }
+    },
+    // TODO: this is Android only; think about how to do this better
+    shuttingDown:false,
+    UsePolling:false,
+    // END TODO
+
+    // TODO: iOS only
+    // This queue holds the currently executing command and all pending
+    // commands executed with cordova.exec().
+    commandQueue:[],
+    // Indicates if we're currently in the middle of flushing the command
+    // queue on the native side.
+    commandQueueFlushing:false,
+    // END TODO
+    /**
+     * Plugin callback mechanism.
+     */
+    callbackId: 0,
+    callbacks:  {},
+    callbackStatus: {
+        NO_RESULT: 0,
+        OK: 1,
+        CLASS_NOT_FOUND_EXCEPTION: 2,
+        ILLEGAL_ACCESS_EXCEPTION: 3,
+        INSTANTIATION_EXCEPTION: 4,
+        MALFORMED_URL_EXCEPTION: 5,
+        IO_EXCEPTION: 6,
+        INVALID_ACTION: 7,
+        JSON_EXCEPTION: 8,
+        ERROR: 9
+    },
+
+    /**
+     * Called by native code when returning successful result from an action.
+     *
+     * @param callbackId
+     * @param args
+     */
+    callbackSuccess: function(callbackId, args) {
+        if (cordova.callbacks[callbackId]) {
+
+            // If result is to be sent to callback
+            if (args.status == cordova.callbackStatus.OK) {
+                try {
+                    if (cordova.callbacks[callbackId].success) {
+                        cordova.callbacks[callbackId].success(args.message);
+                    }
+                }
+                catch (e) {
+                    console.log("Error in success callback: "+callbackId+" = "+e);
+                }
+            }
+
+            // Clear callback if not expecting any more results
+            if (!args.keepCallback) {
+                delete cordova.callbacks[callbackId];
+            }
+        }
+    },
+
+    /**
+     * Called by native code when returning error result from an action.
+     *
+     * @param callbackId
+     * @param args
+     */
+    callbackError: function(callbackId, args) {
+        if (cordova.callbacks[callbackId]) {
+            try {
+                if (cordova.callbacks[callbackId].fail) {
+                    cordova.callbacks[callbackId].fail(args.message);
+                }
+            }
+            catch (e) {
+                console.log("Error in error callback: "+callbackId+" = "+e);
+            }
+
+            // Clear callback if not expecting any more results
+            if (!args.keepCallback) {
+                delete cordova.callbacks[callbackId];
+            }
+        }
+    },
+    addConstructor: function(func) {
+        channel.onCordovaReady.subscribeOnce(function() {
+            try {
+                func();
+            } catch(e) {
+                console.log("Failed to run constructor: " + e);
+            }
+        });
+    }
+};
+
+// Register pause, resume and deviceready channels as events on document.
+channel.onPause = cordova.addDocumentEventHandler('pause');
+channel.onResume = cordova.addDocumentEventHandler('resume');
+channel.onDeviceReady = cordova.addDocumentEventHandler('deviceready');
+
+module.exports = cordova;
+
+});
+
+// file: lib/common/builder.js
+define("cordova/builder", function(require, exports, module) {
+var utils = require('cordova/utils');
+
+function each(objects, func, context) {
+    for (var prop in objects) {
+        if (objects.hasOwnProperty(prop)) {
+            func.apply(context, [objects[prop], prop]);
+        }
+    }
+}
+
+function include(parent, objects, clobber, merge) {
+    each(objects, function (obj, key) {
+        try {
+          var result = obj.path ? require(obj.path) : {};
+
+          if (clobber) {
+              // Clobber if it doesn't exist.
+              if (typeof parent[key] === 'undefined') {
+                  parent[key] = result;
+              } else if (typeof obj.path !== 'undefined') {
+                  // If merging, merge properties onto parent, otherwise, clobber.
+                  if (merge) {
+                      recursiveMerge(parent[key], result);
+                  } else {
+                      parent[key] = result;
+                  }
+              }
+              result = parent[key];
+          } else {
+            // Overwrite if not currently defined.
+            if (typeof parent[key] == 'undefined') {
+              parent[key] = result;
+            } else if (merge && typeof obj.path !== 'undefined') {
+              // If merging, merge parent onto result
+              recursiveMerge(result, parent[key]);
+              parent[key] = result;
+            } else {
+              // Set result to what already exists, so we can build children into it if they exist.
+              result = parent[key];
+            }
+          }
+
+          if (obj.children) {
+            include(result, obj.children, clobber, merge);
+          }
+        } catch(e) {
+          utils.alert('Exception building cordova JS globals: ' + e + ' for key "' + key + '"');
+        }
+    });
+}
+
+/**
+ * Merge properties from one object onto another recursively.  Properties from
+ * the src object will overwrite existing target property.
+ *
+ * @param target Object to merge properties into.
+ * @param src Object to merge properties from.
+ */
+function recursiveMerge(target, src) {
+    for (var prop in src) {
+        if (src.hasOwnProperty(prop)) {
+            if (typeof target.prototype !== 'undefined' && target.prototype.constructor === target) {
+                // If the target object is a constructor override off prototype.
+                target.prototype[prop] = src[prop];
+            } else {
+                target[prop] = typeof src[prop] === 'object' ? recursiveMerge(
+                        target[prop], src[prop]) : src[prop];
+            }
+        }
+    }
+    return target;
+}
+
+module.exports = {
+    build: function (objects) {
+        return {
+            intoButDontClobber: function (target) {
+                include(target, objects, false, false);
+            },
+            intoAndClobber: function(target) {
+                include(target, objects, true, false);
+            },
+            intoAndMerge: function(target) {
+                include(target, objects, true, true);
+            }
+        };
+    }
+};
+
+});
+
+// file: lib/common/channel.js
+define("cordova/channel", function(require, exports, module) {
+var utils = require('cordova/utils');
+
+/**
+ * Custom pub-sub "channel" that can have functions subscribed to it
+ * This object is used to define and control firing of events for
+ * cordova initialization.
+ *
+ * The order of events during page load and Cordova startup is as follows:
+ *
+ * onDOMContentLoaded         Internal event that is received when the web page is loaded and parsed.
+ * onNativeReady              Internal event that indicates the Cordova native side is ready.
+ * onCordovaReady             Internal event fired when all Cordova JavaScript objects have been created.
+ * onCordovaInfoReady         Internal event fired when device properties are available.
+ * onCordovaConnectionReady   Internal event fired when the connection property has been set.
+ * onDeviceReady              User event fired to indicate that Cordova is ready
+ * onResume                   User event fired to indicate a start/resume lifecycle event
+ * onPause                    User event fired to indicate a pause lifecycle event
+ * onDestroy                  Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
+ *
+ * The only Cordova events that user code should register for are:
+ *      deviceready           Cordova native code is initialized and Cordova APIs can be called from JavaScript
+ *      pause                 App has moved to background
+ *      resume                App has returned to foreground
+ *
+ * Listeners can be registered as:
+ *      document.addEventListener("deviceready", myDeviceReadyListener, false);
+ *      document.addEventListener("resume", myResumeListener, false);
+ *      document.addEventListener("pause", myPauseListener, false);
+ *
+ * The DOM lifecycle events should be used for saving and restoring state
+ *      window.onload
+ *      window.onunload
+ *
+ */
+
+/**
+ * Channel
+ * @constructor
+ * @param type  String the channel name
+ * @param opts  Object options to pass into the channel, currently
+ *                     supports:
+ *                     onSubscribe: callback that fires when
+ *                       something subscribes to the Channel. Sets
+ *                       context to the Channel.
+ *                     onUnsubscribe: callback that fires when
+ *                       something unsubscribes to the Channel. Sets
+ *                       context to the Channel.
+ */
+var Channel = function(type, opts) {
+    this.type = type;
+    this.handlers = {};
+    this.numHandlers = 0;
+    this.guid = 1;
+    this.fired = false;
+    this.enabled = true;
+    this.events = {
+        onSubscribe:null,
+        onUnsubscribe:null
+    };
+    if (opts) {
+        if (opts.onSubscribe) this.events.onSubscribe = opts.onSubscribe;
+        if (opts.onUnsubscribe) this.events.onUnsubscribe = opts.onUnsubscribe;
+    }
+},
+    channel = {
+        /**
+         * Calls the provided function only after all of the channels specified
+         * have been fired.
+         */
+        join: function (h, c) {
+            var i = c.length;
+            var len = i;
+            var f = function() {
+                if (!(--i)) h();
+            };
+            for (var j=0; j<len; j++) {
+                !c[j].fired?c[j].subscribeOnce(f):i--;
+            }
+            if (!i) h();
+        },
+        create: function (type, opts) {
+            channel[type] = new Channel(type, opts);
+            return channel[type];
+        },
+
+        /**
+         * cordova Channels that must fire before "deviceready" is fired.
+         */
+        deviceReadyChannelsArray: [],
+        deviceReadyChannelsMap: {},
+
+        /**
+         * Indicate that a feature needs to be initialized before it is ready to be used.
+         * This holds up Cordova's "deviceready" event until the feature has been initialized
+         * and Cordova.initComplete(feature) is called.
+         *
+         * @param feature {String}     The unique feature name
+         */
+        waitForInitialization: function(feature) {
+            if (feature) {
+                var c = null;
+                if (this[feature]) {
+                    c = this[feature];
+                }
+                else {
+                    c = this.create(feature);
+                }
+                this.deviceReadyChannelsMap[feature] = c;
+                this.deviceReadyChannelsArray.push(c);
+            }
+        },
+
+        /**
+         * Indicate that initialization code has completed and the feature is ready to be used.
+         *
+         * @param feature {String}     The unique feature name
+         */
+        initializationComplete: function(feature) {
+            var c = this.deviceReadyChannelsMap[feature];
+            if (c) {
+                c.fire();
+            }
+        }
+    };
+
+function forceFunction(f) {
+    if (f === null || f === undefined || typeof f != 'function') throw "Function required as first argument!";
+}
+
+/**
+ * Subscribes the given function to the channel. Any time that
+ * Channel.fire is called so too will the function.
+ * Optionally specify an execution context for the function
+ * and a guid that can be used to stop subscribing to the channel.
+ * Returns the guid.
+ */
+Channel.prototype.subscribe = function(f, c, g) {
+    // need a function to call
+    forceFunction(f);
+
+    var func = f;
+    if (typeof c == "object") { func = utils.close(c, f); }
+
+    g = g || func.observer_guid || f.observer_guid;
+    if (!g) {
+        // first time we've seen this subscriber
+        g = this.guid++;
+    }
+    else {
+        // subscriber already handled; dont set it twice
+        return g;
+    }
+    func.observer_guid = g;
+    f.observer_guid = g;
+    this.handlers[g] = func;
+    this.numHandlers++;
+    if (this.events.onSubscribe) this.events.onSubscribe.call(this);
+    if (this.fired) func.call(this);
+    return g;
+};
+
+/**
+ * Like subscribe but the function is only called once and then it
+ * auto-unsubscribes itself.
+ */
+Channel.prototype.subscribeOnce = function(f, c) {
+    // need a function to call
+    forceFunction(f);
+
+    var g = null;
+    var _this = this;
+    var m = function() {
+        f.apply(c || null, arguments);
+        _this.unsubscribe(g);
+    };
+    if (this.fired) {
+        if (typeof c == "object") { f = utils.close(c, f); }
+        f.apply(this, this.fireArgs);
+    } else {
+        g = this.subscribe(m);
+    }
+    return g;
+};
+
+/**
+ * Unsubscribes the function with the given guid from the channel.
+ */
+Channel.prototype.unsubscribe = function(g) {
+    // need a function to unsubscribe
+    if (g === null || g === undefined) { throw "You must pass _something_ into Channel.unsubscribe"; }
+
+    if (typeof g == 'function') { g = g.observer_guid; }
+    var handler = this.handlers[g];
+    if (handler) {
+        if (handler.observer_guid) handler.observer_guid=null;
+        this.handlers[g] = null;
+        delete this.handlers[g];
+        this.numHandlers--;
+        if (this.events.onUnsubscribe) this.events.onUnsubscribe.call(this);
+    }
+};
+
+/**
+ * Calls all functions subscribed to this channel.
+ */
+Channel.prototype.fire = function(e) {
+    if (this.enabled) {
+        var fail = false;
+        this.fired = true;
+        for (var item in this.handlers) {
+            var handler = this.handlers[item];
+            if (typeof handler == 'function') {
+                var rv = (handler.apply(this, arguments)===false);
+                fail = fail || rv;
+            }
+        }
+        this.fireArgs = arguments;
+        return !fail;
+    }
+    return true;
+};
+
+// defining them here so they are ready super fast!
+// DOM event that is received when the web page is loaded and parsed.
+channel.create('onDOMContentLoaded');
+
+// Event to indicate the Cordova native side is ready.
+channel.create('onNativeReady');
+
+// Event to indicate that all Cordova JavaScript objects have been created
+// and it's time to run plugin constructors.
+channel.create('onCordovaReady');
+
+// Event to indicate that device properties are available
+channel.create('onCordovaInfoReady');
+
+// Event to indicate that the connection property has been set.
+channel.create('onCordovaConnectionReady');
+
+// Event to indicate that Cordova is ready
+channel.create('onDeviceReady');
+
+// Event to indicate a resume lifecycle event
+channel.create('onResume');
+
+// Event to indicate a pause lifecycle event
+channel.create('onPause');
+
+// Event to indicate a destroy lifecycle event
+channel.create('onDestroy');
+
+// Channels that must fire before "deviceready" is fired.
+channel.waitForInitialization('onCordovaReady');
+channel.waitForInitialization('onCordovaConnectionReady');
+
+module.exports = channel;
+
+});
+
+// file: lib/common/common.js
+define("cordova/common", function(require, exports, module) {
+module.exports = {
+    objects: {
+        cordova: {
+            path: 'cordova',
+            children: {
+                exec: {
+                    path: 'cordova/exec'
+                },
+                logger: {
+                    path: 'cordova/plugin/logger'
+                }
+            }
+        },
+        Cordova: {
+            children: {
+                exec: {
+                    path: 'cordova/exec'
+                }
+            }
+        },
+        PhoneGap:{
+            children: {
+                exec: {
+                    path: 'cordova/exec'
+                }
+            }
+        },
+        navigator: {
+            children: {
+                notification: {
+                    path: 'cordova/plugin/notification'
+                },
+                accelerometer: {
+                    path: 'cordova/plugin/accelerometer'
+                },
+                battery: {
+                    path: 'cordova/plugin/battery'
+                },
+                camera:{
+                    path: 'cordova/plugin/Camera'
+                },
+                compass:{
+                    path: 'cordova/plugin/compass'
+                },
+                contacts: {
+                    path: 'cordova/plugin/contacts'
+                },
+                device:{
+                    children:{
+                        capture: {
+                            path: 'cordova/plugin/capture'
+                        }
+                    }
+                },
+                geolocation: {
+                    path: 'cordova/plugin/geolocation'
+                },
+                network: {
+                    children: {
+                        connection: {
+                            path: 'cordova/plugin/network'
+                        }
+                    }
+                },
+                splashscreen: {
+                    path: 'cordova/plugin/splashscreen'
+                }
+            }
+        },
+        Acceleration: {
+            path: 'cordova/plugin/Acceleration'
+        },
+        Camera:{
+            path: 'cordova/plugin/CameraConstants'
+        },
+        CameraPopoverOptions: {
+            path: 'cordova/plugin/CameraPopoverOptions'
+        },
+        CaptureError: {
+            path: 'cordova/plugin/CaptureError'
+        },
+        CaptureAudioOptions:{
+            path: 'cordova/plugin/CaptureAudioOptions'
+        },
+        CaptureImageOptions: {
+            path: 'cordova/plugin/CaptureImageOptions'
+        },
+        CaptureVideoOptions: {
+            path: 'cordova/plugin/CaptureVideoOptions'
+        },
+        CompassHeading:{
+            path: 'cordova/plugin/CompassHeading'
+        },
+        CompassError:{
+            path: 'cordova/plugin/CompassError'
+        },
+        ConfigurationData: {
+            path: 'cordova/plugin/ConfigurationData'
+        },
+        Connection: {
+            path: 'cordova/plugin/Connection'
+        },
+        Contact: {
+            path: 'cordova/plugin/Contact'
+        },
+        ContactAddress: {
+            path: 'cordova/plugin/ContactAddress'
+        },
+        ContactError: {
+            path: 'cordova/plugin/ContactError'
+        },
+        ContactField: {
+            path: 'cordova/plugin/ContactField'
+        },
+        ContactFindOptions: {
+            path: 'cordova/plugin/ContactFindOptions'
+        },
+        ContactName: {
+            path: 'cordova/plugin/ContactName'
+        },
+        ContactOrganization: {
+            path: 'cordova/plugin/ContactOrganization'
+        },
+        Coordinates: {
+            path: 'cordova/plugin/Coordinates'
+        },
+        device: {
+            path: 'cordova/plugin/device'
+        },
+        DirectoryEntry: {
+            path: 'cordova/plugin/DirectoryEntry'
+        },
+        DirectoryReader: {
+            path: 'cordova/plugin/DirectoryReader'
+        },
+        Entry: {
+            path: 'cordova/plugin/Entry'
+        },
+        File: {
+            path: 'cordova/plugin/File'
+        },
+        FileEntry: {
+            path: 'cordova/plugin/FileEntry'
+        },
+        FileError: {
+            path: 'cordova/plugin/FileError'
+        },
+        FileReader: {
+            path: 'cordova/plugin/FileReader'
+        },
+        FileSystem: {
+            path: 'cordova/plugin/FileSystem'
+        },
+        FileTransfer: {
+            path: 'cordova/plugin/FileTransfer'
+        },
+        FileTransferError: {
+            path: 'cordova/plugin/FileTransferError'
+        },
+        FileUploadOptions: {
+            path: 'cordova/plugin/FileUploadOptions'
+        },
+        FileUploadResult: {
+            path: 'cordova/plugin/FileUploadResult'
+        },
+        FileWriter: {
+            path: 'cordova/plugin/FileWriter'
+        },
+        Flags: {
+            path: 'cordova/plugin/Flags'
+        },
+        LocalFileSystem: {
+            path: 'cordova/plugin/LocalFileSystem'
+        },
+        Media: {
+            path: 'cordova/plugin/Media'
+        },
+        MediaError: {
+            path: 'cordova/plugin/MediaError'
+        },
+        MediaFile: {
+            path: 'cordova/plugin/MediaFile'
+        },
+        MediaFileData:{
+            path: 'cordova/plugin/MediaFileData'
+        },
+        Metadata:{
+            path: 'cordova/plugin/Metadata'
+        },
+        Position: {
+            path: 'cordova/plugin/Position'
+        },
+        PositionError: {
+            path: 'cordova/plugin/PositionError'
+        },
+        ProgressEvent: {
+            path: 'cordova/plugin/ProgressEvent'
+        },
+        requestFileSystem:{
+            path: 'cordova/plugin/requestFileSystem'
+        },
+        resolveLocalFileSystemURI:{
+            path: 'cordova/plugin/resolveLocalFileSystemURI'
+        }
+    }
+};
+
+});
+
+// file: lib/tizen/exec.js
+define("cordova/exec", function(require, exports, module) {
+/**
+ * Execute a cordova command.  It is up to the native side whether this action
+ * is synchronous or asynchronous.  The native side can return:
+ *      Synchronous: PluginResult object as a JSON string
+ *      Asynchrounous: Empty string ""
+ * If async, the native side will cordova.callbackSuccess or cordova.callbackError,
+ * depending upon the result of the action.
+ *
+ * @param {Function} successCB  The success callback
+ * @param {Function} failCB     The fail callback
+ * @param {String} service      The name of the service to use
+ * @param {String} action       Action to be run in cordova
+ * @param {String[]} [args]     Zero or more arguments to pass to the method
+ */
+
+var tizen = require('cordova/plugin/tizen/manager'),
+    cordova = require('cordova'),
+    utils = require('cordova/utils');
+
+module.exports = function(successCB, failCB, service, action, args) {
+
+    try {
+        var v = tizen.exec(successCB, failCB, service, action, args);
+
+        // If status is OK, then return value back to caller
+        if (v.status == cordova.callbackStatus.OK) {
+
+            // If there is a success callback, then call it now with returned value
+            if (successCB) {
+                try {
+                    successCB(v.message);
+                }
+                catch (e) {
+                    console.log("Error in success callback: "+ service + "." + action + " = " + e);
+                }
+
+            }
+            return v.message;
+        } else if (v.status == cordova.callbackStatus.NO_RESULT) {
+            // Nothing to do here
+        } else {
+            // If error, then display error
+            console.log("Error: " + service + "." + action + " Status=" + v.status + " Message=" + v.message);
+
+            // If there is a fail callback, then call it now with returned value
+            if (failCB) {
+                try {
+                    failCB(v.message);
+                }
+                catch (e) {
+                    console.log("Error in error callback: " + service + "." + action + " = "+e);
+                }
+            }
+            return null;
+        }
+    } catch (e) {
+        utils.alert("Error: " + e);
+    }
+};
+
+});
+
+// file: lib/tizen/platform.js
+define("cordova/platform", function(require, exports, module) {
+module.exports = {
+    id: "tizen",
+    initialize: function() {},
+    objects: {
+        device: {
+            path: "cordova/plugin/tizen/Device"
+        },
+        File: { // exists natively, override
+            path: "cordova/plugin/File"
+        },
+        FileReader: { // exists natively, override
+            path: "cordova/plugin/FileReader"
+        },
+        FileError: { //exists natively, override
+            path: "cordova/plugin/FileError"
+        }
+    },
+    merges: {
+        MediaError: { // exists natively
+            path: "cordova/plugin/tizen/MediaError"
+        },
+        navigator: {
+            children: {
+                device: {
+                    path: "cordova/plugin/tizen/Device"
+                },
+                contacts: {
+                    path: "cordova/plugin/tizen/contacts"
+                },
+               notification: {
+                   path: "cordova/plugin/tizen/Notification"
+               }
+            }
+        },
+        Contact: {
+            path: "cordova/plugin/tizen/Contact"
+        }
+    }
+};
+
+});
+
+// file: lib/common/plugin/Acceleration.js
+define("cordova/plugin/Acceleration", function(require, exports, module) {
+var Acceleration = function(x, y, z, timestamp) {
+    this.x = x;
+    this.y = y;
+    this.z = z;
+    this.timestamp = timestamp || (new Date()).getTime();
+};
+
+module.exports = Acceleration;
+
+});
+
+// file: lib/common/plugin/Camera.js
+define("cordova/plugin/Camera", function(require, exports, module) {
+var exec = require('cordova/exec'),
+    Camera = require('cordova/plugin/CameraConstants');
+
+var cameraExport = {};
+
+// Tack on the Camera Constants to the base camera plugin.
+for (var key in Camera) {
+    cameraExport[key] = Camera[key];
+}
+
+/**
+ * Gets a picture from source defined by "options.sourceType", and returns the
+ * image as defined by the "options.destinationType" option.
+
+ * The defaults are sourceType=CAMERA and destinationType=FILE_URI.
+ *
+ * @param {Function} successCallback
+ * @param {Function} errorCallback
+ * @param {Object} options
+ */
+cameraExport.getPicture = function(successCallback, errorCallback, options) {
+    // successCallback required
+    if (typeof successCallback != "function") {
+        console.log("Camera Error: successCallback is not a function");
+        return;
+    }
+
+    // errorCallback optional
+    if (errorCallback && (typeof errorCallback != "function")) {
+        console.log("Camera Error: errorCallback is not a function");
+        return;
+    }
+
+    var quality = 50;
+    if (options && typeof options.quality == "number") {
+        quality = options.quality;
+    } else if (options && typeof options.quality == "string") {
+        var qlity = parseInt(options.quality, 10);
+        if (isNaN(qlity) === false) {
+            quality = qlity.valueOf();
+        }
+    }
+
+    var destinationType = Camera.DestinationType.FILE_URI;
+    if (typeof options.destinationType == "number") {
+        destinationType = options.destinationType;
+    }
+
+    var sourceType = Camera.PictureSourceType.CAMERA;
+    if (typeof options.sourceType == "number") {
+        sourceType = options.sourceType;
+    }
+
+    var targetWidth = -1;
+    if (typeof options.targetWidth == "number") {
+        targetWidth = options.targetWidth;
+    } else if (typeof options.targetWidth == "string") {
+        var width = parseInt(options.targetWidth, 10);
+        if (isNaN(width) === false) {
+            targetWidth = width.valueOf();
+        }
+    }
+
+    var targetHeight = -1;
+    if (typeof options.targetHeight == "number") {
+        targetHeight = options.targetHeight;
+    } else if (typeof options.targetHeight == "string") {
+        var height = parseInt(options.targetHeight, 10);
+        if (isNaN(height) === false) {
+            targetHeight = height.valueOf();
+        }
+    }
+
+    var encodingType = Camera.EncodingType.JPEG;
+    if (typeof options.encodingType == "number") {
+        encodingType = options.encodingType;
+    }
+
+    var mediaType = Camera.MediaType.PICTURE;
+    if (typeof options.mediaType == "number") {
+        mediaType = options.mediaType;
+    }
+    var allowEdit = false;
+    if (typeof options.allowEdit == "boolean") {
+        allowEdit = options.allowEdit;
+    } else if (typeof options.allowEdit == "number") {
+        allowEdit = options.allowEdit <= 0 ? false : true;
+    }
+    var correctOrientation = false;
+    if (typeof options.correctOrientation == "boolean") {
+        correctOrientation = options.correctOrientation;
+    } else if (typeof options.correctOrientation == "number") {
+        correctOrientation = options.correctOrientation <=0 ? false : true;
+    }
+    var saveToPhotoAlbum = false;
+    if (typeof options.saveToPhotoAlbum == "boolean") {
+        saveToPhotoAlbum = options.saveToPhotoAlbum;
+    } else if (typeof options.saveToPhotoAlbum == "number") {
+        saveToPhotoAlbum = options.saveToPhotoAlbum <=0 ? false : true;
+    }
+    var popoverOptions = null;
+    if (typeof options.popoverOptions == "object") {
+        popoverOptions = options.popoverOptions;
+    }
+
+    var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
+                mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions];
+
+    exec(successCallback, errorCallback, "Camera", "takePicture", args);
+};
+
+cameraExport.cleanup = function(successCallback, errorCallback) {
+    exec(successCallback, errorCallback, "Camera", "cleanup", []);
+};
+
+module.exports = cameraExport;
+});
+
+// file: lib/common/plugin/CameraConstants.js
+define("cordova/plugin/CameraConstants", function(require, exports, module) {
+module.exports = {
+  DestinationType:{
+    DATA_URL: 0,         // Return base64 encoded string
+    FILE_URI: 1          // Return file uri (content://media/external/images/media/2 for Android)
+  },
+  EncodingType:{
+    JPEG: 0,             // Return JPEG encoded image
+    PNG: 1               // Return PNG encoded image
+  },
+  MediaType:{
+    PICTURE: 0,          // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
+    VIDEO: 1,            // allow selection of video only, ONLY RETURNS URL
+    ALLMEDIA : 2         // allow selection from all media types
+  },
+  PictureSourceType:{
+    PHOTOLIBRARY : 0,    // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
+    CAMERA : 1,          // Take picture from camera
+    SAVEDPHOTOALBUM : 2  // Choose image from picture library (same as PHOTOLIBRARY for Android)
+  },
+  PopoverArrowDirection:{
+      ARROW_UP : 1,        // matches iOS UIPopoverArrowDirection constants to specify arrow location on popover
+      ARROW_DOWN : 2,
+      ARROW_LEFT : 4,
+      ARROW_RIGHT : 8,
+      ARROW_ANY : 15
+  }
+};
+});
+
+// file: lib/common/plugin/CameraPopoverOptions.js
+define("cordova/plugin/CameraPopoverOptions", function(require, exports, module) {
+var Camera = require('cordova/plugin/CameraConstants');
+
+/**
+ * Encapsulates options for iOS Popover image picker
+ */
+var CameraPopoverOptions = function(x,y,width,height,arrowDir){
+    // information of rectangle that popover should be anchored to
+    this.x = x || 0;
+    this.y = y || 32;
+    this.width = width || 320;
+    this.height = height || 480;
+    // The direction of the popover arrow
+    this.arrowDir = arrowDir || Camera.PopoverArrowDirection.ARROW_ANY;
+};
+
+module.exports = CameraPopoverOptions;
+});
+
+// file: lib/common/plugin/CaptureAudioOptions.js
+define("cordova/plugin/CaptureAudioOptions", function(require, exports, module) {
+/**
+ * Encapsulates all audio capture operation configuration options.
+ */
+var CaptureAudioOptions = function(){
+    // Upper limit of sound clips user can record. Value must be equal or greater than 1.
+    this.limit = 1;
+    // Maximum duration of a single sound clip in seconds.
+    this.duration = 0;
+    // The selected audio mode. Must match with one of the elements in supportedAudioModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureAudioOptions;
+});
+
+// file: lib/common/plugin/CaptureError.js
+define("cordova/plugin/CaptureError", function(require, exports, module) {
+/**
+ * The CaptureError interface encapsulates all errors in the Capture API.
+ */
+var CaptureError = function(c) {
+   this.code = c || null;
+};
+
+// Camera or microphone failed to capture image or sound.
+CaptureError.CAPTURE_INTERNAL_ERR = 0;
+// Camera application or audio capture application is currently serving other capture request.
+CaptureError.CAPTURE_APPLICATION_BUSY = 1;
+// Invalid use of the API (e.g. limit parameter has value less than one).
+CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
+// User exited camera application or audio capture application before capturing anything.
+CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
+// The requested capture operation is not supported.
+CaptureError.CAPTURE_NOT_SUPPORTED = 20;
+
+module.exports = CaptureError;
+});
+
+// file: lib/common/plugin/CaptureImageOptions.js
+define("cordova/plugin/CaptureImageOptions", function(require, exports, module) {
+/**
+ * Encapsulates all image capture operation configuration options.
+ */
+var CaptureImageOptions = function(){
+    // Upper limit of images user can take. Value must be equal or greater than 1.
+    this.limit = 1;
+    // The selected image mode. Must match with one of the elements in supportedImageModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureImageOptions;
+});
+
+// file: lib/common/plugin/CaptureVideoOptions.js
+define("cordova/plugin/CaptureVideoOptions", function(require, exports, module) {
+/**
+ * Encapsulates all video capture operation configuration options.
+ */
+var CaptureVideoOptions = function(){
+    // Upper limit of videos user can record. Value must be equal or greater than 1.
+    this.limit = 1;
+    // Maximum duration of a single video clip in seconds.
+    this.duration = 0;
+    // The selected video mode. Must match with one of the elements in supportedVideoModes array.
+    this.mode = null;
+};
+
+module.exports = CaptureVideoOptions;
+});
+
+// file: lib/common/plugin/CompassError.js
+define("cordova/plugin/CompassError", function(require, exports, module) {
+/**
+ *  CompassError.
+ *  An error code assigned by an implementation when an error has occured
+ * @constructor
+ */
+var CompassError = function(err) {
+    this.code = (err !== undefined ? err : null);
+};
+
+CompassError.COMPASS_INTERNAL_ERR = 0;
+CompassError.COMPASS_NOT_SUPPORTED = 20;
+
+module.exports = CompassError;
+});
+
+// file: lib/common/plugin/CompassHeading.js
+define("cordova/plugin/CompassHeading", function(require, exports, module) {
+var CompassHeading = function(magneticHeading, trueHeading, headingAccuracy, timestamp) {
+  this.magneticHeading = (magneticHeading !== undefined ? magneticHeading : null);
+  this.trueHeading = (trueHeading !== undefined ? trueHeading : null);
+  this.headingAccuracy = (headingAccuracy !== undefined ? headingAccuracy : null);
+  this.timestamp = (timestamp !== undefined ? timestamp : new Date().getTime());
+};
+
+module.exports = CompassHeading;
+});
+
+// file: lib/common/plugin/ConfigurationData.js
+define("cordova/plugin/ConfigurationData", function(require, exports, module) {
+/**
+ * Encapsulates a set of parameters that the capture device supports.
+ */
+function ConfigurationData() {
+    // The ASCII-encoded string in lower case representing the media type.
+    this.type = null;
+    // The height attribute represents height of the image or video in pixels.
+    // In the case of a sound clip this attribute has value 0.
+    this.height = 0;
+    // The width attribute represents width of the image or video in pixels.
+    // In the case of a sound clip this attribute has value 0
+    this.width = 0;
+}
+
+module.exports = ConfigurationData;
+});
+
+// file: lib/common/plugin/Connection.js
+define("cordova/plugin/Connection", function(require, exports, module) {
+/**
+ * Network status
+ */
+module.exports = {
+        UNKNOWN: "unknown",
+        ETHERNET: "ethernet",
+        WIFI: "wifi",
+        CELL_2G: "2g",
+        CELL_3G: "3g",
+        CELL_4G: "4g",
+        NONE: "none"
+};
+});
+
+// file: lib/common/plugin/Contact.js
+define("cordova/plugin/Contact", function(require, exports, module) {
+var exec = require('cordova/exec'),
+    ContactError = require('cordova/plugin/ContactError'),
+    utils = require('cordova/utils');
+
+/**
+* Converts primitives into Complex Object
+* Currently only used for Date fields
+*/
+function convertIn(contact) {
+    var value = contact.birthday;
+    try {
+      contact.birthday = new Date(parseFloat(value));
+    } catch (exception){
+      console.log("Cordova Contact convertIn error: exception creating date.");
+    }
+    return contact;
+}
+
+/**
+* Converts Complex objects into primitives
+* Only conversion at present is for Dates.
+**/
+
+function convertOut(contact) {
+    var value = contact.birthday;
+    if (value !== null) {
+        // try to make it a Date object if it is not already
+        if (!utils.isDate(value)){
+            try {
+                value = new Date(value);
+            } catch(exception){
+                value = null;
+            }
+        }
+        if (utils.isDate(value)){
+            value = value.valueOf(); // convert to milliseconds
+        }
+        contact.birthday = value;
+    }
+    return contact;
+}
+
+/**
+* Contains information about a single contact.
+* @constructor
+* @param {DOMString} id unique identifier
+* @param {DOMString} displayName
+* @param {ContactName} name
+* @param {DOMString} nickname
+* @param {Array.<ContactField>} phoneNumbers array of phone numbers
+* @param {Array.<ContactField>} emails array of email addresses
+* @param {Array.<ContactAddress>} addresses array of addresses
+* @param {Array.<ContactField>} ims instant messaging user ids
+* @param {Array.<ContactOrganization>} organizations
+* @param {DOMString} birthday contact's birthday
+* @param {DOMString} note user notes about contact
+* @param {Array.<ContactField>} photos
+* @param {Array.<ContactField>} categories
+* @param {Array.<ContactField>} urls contact's web sites
+*/
+var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
+    ims, organizations, birthday, note, photos, categories, urls) {
+    this.id = id || null;
+    this.rawId = null;
+    this.displayName = displayName || null;
+    this.name = name || null; // ContactName
+    this.nickname = nickname || null;
+    this.phoneNumbers = phoneNumbers || null; // ContactField[]
+    this.emails = emails || null; // ContactField[]
+    this.addresses = addresses || null; // ContactAddress[]
+    this.ims = ims || null; // ContactField[]
+    this.organizations = organizations || null; // ContactOrganization[]
+    this.birthday = birthday || null;
+    this.note = note || null;
+    this.photos = photos || null; // ContactField[]
+    this.categories = categories || null; // ContactField[]
+    this.urls = urls || null; // ContactField[]
+};
+
+/**
+* Removes contact from device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.remove = function(successCB, errorCB) {
+    var fail = function(code) {
+        errorCB(new ContactError(code));
+    };
+    if (this.id === null) {
+        fail(ContactError.UNKNOWN_ERROR);
+    }
+    else {
+        exec(successCB, fail, "Contacts", "remove", [this.id]);
+    }
+};
+
+/**
+* Creates a deep copy of this Contact.
+* With the contact ID set to null.
+* @return copy of this Contact
+*/
+Contact.prototype.clone = function() {
+    var clonedContact = utils.clone(this);
+    var i;
+    clonedContact.id = null;
+    clonedContact.rawId = null;
+    // Loop through and clear out any id's in phones, emails, etc.
+    if (clonedContact.phoneNumbers) {
+        for (i = 0; i < clonedContact.phoneNumbers.length; i++) {
+            clonedContact.phoneNumbers[i].id = null;
+        }
+    }
+    if (clonedContact.emails) {
+        for (i = 0; i < clonedContact.emails.length; i++) {
+            clonedContact.emails[i].id = null;
+        }
+    }
+    if (clonedContact.addresses) {
+        for (i = 0; i < clonedContact.addresses.length; i++) {
+            clonedContact.addresses[i].id = null;
+        }
+    }
+    if (clonedContact.ims) {
+        for (i = 0; i < clonedContact.ims.length; i++) {
+            clonedContact.ims[i].id = null;
+        }
+    }
+    if (clonedContact.organizations) {
+        for (i = 0; i < clonedContact.organizations.length; i++) {
+            clonedContact.organizations[i].id = null;
+        }
+    }
+    if (clonedContact.categories) {
+        for (i = 0; i < clonedContact.categories.length; i++) {
+            clonedContact.categories[i].id = null;
+        }
+    }
+    if (clonedContact.photos) {
+        for (i = 0; i < clonedContact.photos.length; i++) {
+            clonedContact.photos[i].id = null;
+        }
+    }
+    if (clonedContact.urls) {
+        for (i = 0; i < clonedContact.urls.length; i++) {
+            clonedContact.urls[i].id = null;
+        }
+    }
+    return clonedContact;
+};
+
+/**
+* Persists contact to device storage.
+* @param successCB success callback
+* @param errorCB error callback
+*/
+Contact.prototype.save = function(successCB, errorCB) {
+  var fail = function(code) {
+      errorCB(new ContactError(code));
+  };
+    var success = function(result) {
+      if (result) {
+          if (typeof successCB === 'function') {
+              var fullContact = require('cordova/plugin/contacts').create(result);
+              successCB(convertIn(fullContact));
+          }
+      }
+      else {
+          // no Entry object returned
+          fail(ContactError.UNKNOWN_ERROR);
+      }
+  };
+    var dupContact = convertOut(utils.clone(this));
+    exec(success, fail, "Contacts", "save", [dupContact]);
+};
+
+
+module.exports = Contact;
+
+});
+
+// file: lib/common/plugin/ContactAddress.js
+define("cordova/plugin/ContactAddress", function(require, exports, module) {
+/**
+* Contact address.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code
+* @param formatted // NOTE: not a W3C standard
+* @param streetAddress
+* @param locality
+* @param region
+* @param postalCode
+* @param country
+*/
+
+var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) {
+    this.id = null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+    this.type = type || null;
+    this.formatted = formatted || null;
+    this.streetAddress = streetAddress || null;
+    this.locality = locality || null;
+    this.region = region || null;
+    this.postalCode = postalCode || null;
+    this.country = country || null;
+};
+
+module.exports = ContactAddress;
+});
+
+// file: lib/common/plugin/ContactError.js
+define("cordova/plugin/ContactError", function(require, exports, module) {
+/**
+ *  ContactError.
+ *  An error code assigned by an implementation when an error has occured
+ * @constructor
+ */
+var ContactError = function(err) {
+    this.code = (typeof err != 'undefined' ? err : null);
+};
+
+/**
+ * Error codes
+ */
+ContactError.UNKNOWN_ERROR = 0;
+ContactError.INVALID_ARGUMENT_ERROR = 1;
+ContactError.TIMEOUT_ERROR = 2;
+ContactError.PENDING_OPERATION_ERROR = 3;
+ContactError.IO_ERROR = 4;
+ContactError.NOT_SUPPORTED_ERROR = 5;
+ContactError.PERMISSION_DENIED_ERROR = 20;
+
+module.exports = ContactError;
+});
+
+// file: lib/common/plugin/ContactField.js
+define("cordova/plugin/ContactField", function(require, exports, module) {
+/**
+* Generic contact field.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param type
+* @param value
+* @param pref
+*/
+var ContactField = function(type, value, pref) {
+    this.id = null;
+    this.type = (type && type.toString()) || null;
+    this.value = (value && value.toString()) || null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+};
+
+module.exports = ContactField;
+});
+
+// file: lib/common/plugin/ContactFindOptions.js
+define("cordova/plugin/ContactFindOptions", function(require, exports, module) {
+/**
+ * ContactFindOptions.
+ * @constructor
+ * @param filter used to match contacts against
+ * @param multiple boolean used to determine if more than one contact should be returned
+ */
+
+var ContactFindOptions = function(filter, multiple) {
+    this.filter = filter || '';
+    this.multiple = (typeof multiple != 'undefined' ? multiple : false);
+};
+
+module.exports = ContactFindOptions;
+});
+
+// file: lib/common/plugin/ContactName.js
+define("cordova/plugin/ContactName", function(require, exports, module) {
+/**
+* Contact name.
+* @constructor
+* @param formatted // NOTE: not part of W3C standard
+* @param familyName
+* @param givenName
+* @param middle
+* @param prefix
+* @param suffix
+*/
+var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
+    this.formatted = formatted || null;
+    this.familyName = familyName || null;
+    this.givenName = givenName || null;
+    this.middleName = middle || null;
+    this.honorificPrefix = prefix || null;
+    this.honorificSuffix = suffix || null;
+};
+
+module.exports = ContactName;
+});
+
+// file: lib/common/plugin/ContactOrganization.js
+define("cordova/plugin/ContactOrganization", function(require, exports, module) {
+/**
+* Contact organization.
+* @constructor
+* @param {DOMString} id unique identifier, should only be set by native code // NOTE: not a W3C standard
+* @param name
+* @param dept
+* @param title
+* @param startDate
+* @param endDate
+* @param location
+* @param desc
+*/
+
+var ContactOrganization = function(pref, type, name, dept, title) {
+    this.id = null;
+    this.pref = (typeof pref != 'undefined' ? pref : false);
+    this.type = type || null;
+    this.name = name || null;
+    this.department = dept || null;
+    this.title = title || null;
+};
+
+module.exports = ContactOrganization;
+});
+
+// file: lib/common/plugin/Coordinates.js
+define("cordova/plugin/Coordinates", function(require, exports, module) {
+/**
+ * This class contains position information.
+ * @param {Object} lat
+ * @param {Object} lng
+ * @param {Object} alt
+ * @param {Object} acc
+ * @param {Object} head
+ * @param {Object} vel
+ * @param {Object} altacc
+ * @constructor
+ */
+var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
+    /**
+     * The latitude of the position.
+     */
+    this.latitude = lat;
+    /**
+     * The longitude of the position,
+     */
+    this.longitude = lng;
+    /**
+     * The accuracy of the position.
+     */
+    this.accuracy = acc;
+    /**
+     * The altitude of the position.
+     */
+    this.altitude = (alt !== undefined ? alt : null);
+    /**
+     * The direction the device is moving at the position.
+     */
+    this.heading = (head !== undefined ? head : null);
+    /**
+     * The velocity with which the device is moving at the position.
+     */
+    this.speed = (vel !== undefined ? vel : null);
+
+    if (this.speed === 0 || this.speed === null) {
+        this.heading = NaN;
+    }
+
+    /**
+     * The altitude accuracy of the position.
+     */
+    this.altitudeAccuracy = (altacc !== undefined) ? altacc : null;
+};
+
+module.exports = Coordinates;
+
+});
+
+// file: lib/common/plugin/DirectoryEntry.js
+define("cordova/plugin/DirectoryEntry", function(require, exports, module) {
+var utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    Entry = require('cordova/plugin/Entry'),
+    FileError = require('cordova/plugin/FileError'),
+    DirectoryReader = require('cordova/plugin/DirectoryReader');
+
+/**
+ * An interface representing a directory on the file system.
+ *
+ * {boolean} isFile always false (readonly)
+ * {boolean} isDirectory always true (readonly)
+ * {DOMString} name of the directory, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the directory (readonly)
+ * TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly)
+ */
+var DirectoryEntry = function(name, fullPath) {
+     DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]);
+};
+
+utils.extend(DirectoryEntry, Entry);
+
+/**
+ * Creates a new DirectoryReader to read entries from this directory
+ */
+DirectoryEntry.prototype.createReader = function() {
+    return new DirectoryReader(this.fullPath);
+};
+
+/**
+ * Creates or looks up a directory
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
+ * @param {Flags} options to create or excluively create the directory
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getDirectory = function(path, options, successCallback, errorCallback) {
+    var win = typeof successCallback !== 'function' ? null : function(result) {
+        var entry = new DirectoryEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = typeof errorCallback !== 'function' ? null : function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getDirectory", [this.fullPath, path, options]);
+};
+
+/**
+ * Deletes a directory and all of it's contents
+ *
+ * @param {Function} successCallback is called with no parameters
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCallback) {
+    var fail = typeof errorCallback !== 'function' ? null : function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(successCallback, fail, "File", "removeRecursively", [this.fullPath]);
+};
+
+/**
+ * Creates or looks up a file
+ *
+ * @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
+ * @param {Flags} options to create or excluively create the file
+ * @param {Function} successCallback is called with the new entry
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryEntry.prototype.getFile = function(path, options, successCallback, errorCallback) {
+    var win = typeof successCallback !== 'function' ? null : function(result) {
+        var FileEntry = require('cordova/plugin/FileEntry');
+        var entry = new FileEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = typeof errorCallback !== 'function' ? null : function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getFile", [this.fullPath, path, options]);
+};
+
+module.exports = DirectoryEntry;
+
+});
+
+// file: lib/common/plugin/DirectoryReader.js
+define("cordova/plugin/DirectoryReader", function(require, exports, module) {
+var exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError') ;
+
+/**
+ * An interface that lists the files and directories in a directory.
+ */
+function DirectoryReader(path) {
+    this.path = path || null;
+}
+
+/**
+ * Returns a list of entries from a directory.
+ *
+ * @param {Function} successCallback is called with a list of entries
+ * @param {Function} errorCallback is called with a FileError
+ */
+DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
+    var win = typeof successCallback !== 'function' ? null : function(result) {
+        var retVal = [];
+        for (var i=0; i<result.length; i++) {
+            var entry = null;
+            if (result[i].isDirectory) {
+                entry = new (require('cordova/plugin/DirectoryEntry'))();
+            }
+            else if (result[i].isFile) {
+                entry = new (require('cordova/plugin/FileEntry'))();
+            }
+            entry.isDirectory = result[i].isDirectory;
+            entry.isFile = result[i].isFile;
+            entry.name = result[i].name;
+            entry.fullPath = result[i].fullPath;
+            retVal.push(entry);
+        }
+        successCallback(retVal);
+    };
+    var fail = typeof errorCallback !== 'function' ? null : function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "readEntries", [this.path]);
+};
+
+module.exports = DirectoryReader;
+
+});
+
+// file: lib/common/plugin/Entry.js
+define("cordova/plugin/Entry", function(require, exports, module) {
+var exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError'),
+    Metadata = require('cordova/plugin/Metadata');
+
+/**
+ * Represents a file or directory on the local file system.
+ *
+ * @param isFile
+ *            {boolean} true if Entry is a file (readonly)
+ * @param isDirectory
+ *            {boolean} true if Entry is a directory (readonly)
+ * @param name
+ *            {DOMString} name of the file or directory, excluding the path
+ *            leading to it (readonly)
+ * @param fullPath
+ *            {DOMString} the absolute full path to the file or directory
+ *            (readonly)
+ */
+function Entry(isFile, isDirectory, name, fullPath, fileSystem) {
+    this.isFile = (typeof isFile != 'undefined'?isFile:false);
+    this.isDirectory = (typeof isDirectory != 'undefined'?isDirectory:false);
+    this.name = name || '';
+    this.fullPath = fullPath || '';
+    this.filesystem = fileSystem || null;
+}
+
+/**
+ * Look up the metadata of the entry.
+ *
+ * @param successCallback
+ *            {Function} is called with a Metadata object
+ * @param errorCallback
+ *            {Function} is called with a FileError
+ */
+Entry.prototype.getMetadata = function(successCallback, errorCallback) {
+  var success = typeof successCallback !== 'function' ? null : function(lastModified) {
+      var metadata = new Metadata(lastModified);
+      successCallback(metadata);
+  };
+  var fail = typeof errorCallback !== 'function' ? null : function(code) {
+      errorCallback(new FileError(code));
+  };
+
+  exec(success, fail, "File", "getMetadata", [this.fullPath]);
+};
+
+/**
+ * Set the metadata of the entry.
+ *
+ * @param successCallback
+ *            {Function} is called with a Metadata object
+ * @param errorCallback
+ *            {Function} is called with a FileError
+ * @param metadataObject
+ *            {Object} keys and values to set
+ */
+Entry.prototype.setMetadata = function(successCallback, errorCallback, metadataObject) {
+
+  exec(successCallback, errorCallback, "File", "setMetadata", [this.fullPath, metadataObject]);
+};
+
+/**
+ * Move a file or directory to a new location.
+ *
+ * @param parent
+ *            {DirectoryEntry} the directory to which to move this entry
+ * @param newName
+ *            {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ *            {Function} called with the new DirectoryEntry object
+ * @param errorCallback
+ *            {Function} called with a FileError
+ */
+Entry.prototype.moveTo = function(parent, newName, successCallback, errorCallback) {
+    var fail = function(code) {
+        if (typeof errorCallback === 'function') {
+            errorCallback(new FileError(code));
+        }
+    };
+    // user must specify parent Entry
+    if (!parent) {
+        fail(FileError.NOT_FOUND_ERR);
+        return;
+    }
+    // source path
+    var srcPath = this.fullPath,
+        // entry name
+        name = newName || this.name,
+        success = function(entry) {
+            if (entry) {
+                if (typeof successCallback === 'function') {
+                    // create appropriate Entry object
+                    var result = (entry.isDirectory) ? new (require('cordova/plugin/DirectoryEntry'))(entry.name, entry.fullPath) : new (require('cordova/plugin/FileEntry'))(entry.name, entry.fullPath);
+                    try {
+                        successCallback(result);
+                    }
+                    catch (e) {
+                        console.log('Error invoking callback: ' + e);
+                    }
+                }
+            }
+            else {
+                // no Entry object returned
+                fail(FileError.NOT_FOUND_ERR);
+            }
+        };
+
+    // copy
+    exec(success, fail, "File", "moveTo", [srcPath, parent.fullPath, name]);
+};
+
+/**
+ * Copy a directory to a different location.
+ *
+ * @param parent
+ *            {DirectoryEntry} the directory to which to copy the entry
+ * @param newName
+ *            {DOMString} new name of the entry, defaults to the current name
+ * @param successCallback
+ *            {Function} called with the new Entry object
+ * @param errorCallback
+ *            {Function} called with a FileError
+ */
+Entry.prototype.copyTo = function(parent, newName, successCallback, errorCallback) {
+    var fail = function(code) {
+        if (typeof errorCallback === 'function') {
+            errorCallback(new FileError(code));
+        }
+    };
+
+    // user must specify parent Entry
+    if (!parent) {
+        fail(FileError.NOT_FOUND_ERR);
+        return;
+    }
+
+        // source path
+    var srcPath = this.fullPath,
+        // entry name
+        name = newName || this.name,
+        // success callback
+        success = function(entry) {
+            if (entry) {
+                if (typeof successCallback === 'function') {
+                    // create appropriate Entry object
+                    var result = (entry.isDirectory) ? new (require('cordova/plugin/DirectoryEntry'))(entry.name, entry.fullPath) : new (require('cordova/plugin/FileEntry'))(entry.name, entry.fullPath);
+                    try {
+                        successCallback(result);
+                    }
+                    catch (e) {
+                        console.log('Error invoking callback: ' + e);
+                    }
+                }
+            }
+            else {
+                // no Entry object returned
+                fail(FileError.NOT_FOUND_ERR);
+            }
+        };
+
+    // copy
+    exec(success, fail, "File", "copyTo", [srcPath, parent.fullPath, name]);
+};
+
+/**
+ * Return a URL that can be used to identify this entry.
+ */
+Entry.prototype.toURL = function() {
+    // fullPath attribute contains the full URL
+    return this.fullPath;
+};
+
+/**
+ * Returns a URI that can be used to identify this entry.
+ *
+ * @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
+ * @return uri
+ */
+Entry.prototype.toURI = function(mimeType) {
+    console.log("DEPRECATED: Update your code to use 'toURL'");
+    // fullPath attribute contains the full URI
+    return this.toURL();
+};
+
+/**
+ * Remove a file or directory. It is an error to attempt to delete a
+ * directory that is not empty. It is an error to attempt to delete a
+ * root directory of a file system.
+ *
+ * @param successCallback {Function} called with no parameters
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.remove = function(successCallback, errorCallback) {
+    var fail = typeof errorCallback !== 'function' ? null : function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(successCallback, fail, "File", "remove", [this.fullPath]);
+};
+
+/**
+ * Look up the parent DirectoryEntry of this entry.
+ *
+ * @param successCallback {Function} called with the parent DirectoryEntry object
+ * @param errorCallback {Function} called with a FileError
+ */
+Entry.prototype.getParent = function(successCallback, errorCallback) {
+    var win = typeof successCallback !== 'function' ? null : function(result) {
+        var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
+        var entry = new DirectoryEntry(result.name, result.fullPath);
+        successCallback(entry);
+    };
+    var fail = typeof errorCallback !== 'function' ? null : function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getParent", [this.fullPath]);
+};
+
+module.exports = Entry;
+});
+
+// file: lib/common/plugin/File.js
+define("cordova/plugin/File", function(require, exports, module) {
+/**
+ * Constructor.
+ * name {DOMString} name of the file, without path information
+ * fullPath {DOMString} the full path of the file, including the name
+ * type {DOMString} mime type
+ * lastModifiedDate {Date} last modified date
+ * size {Number} size of the file in bytes
+ */
+
+var File = function(name, fullPath, type, lastModifiedDate, size){
+    this.name = name || '';
+    this.fullPath = fullPath || null;
+    this.type = type || null;
+    this.lastModifiedDate = lastModifiedDate || null;
+    this.size = size || 0;
+};
+
+module.exports = File;
+});
+
+// file: lib/common/plugin/FileEntry.js
+define("cordova/plugin/FileEntry", function(require, exports, module) {
+var utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    Entry = require('cordova/plugin/Entry'),
+    FileWriter = require('cordova/plugin/FileWriter'),
+    File = require('cordova/plugin/File'),
+    FileError = require('cordova/plugin/FileError');
+
+/**
+ * An interface representing a file on the file system.
+ *
+ * {boolean} isFile always true (readonly)
+ * {boolean} isDirectory always false (readonly)
+ * {DOMString} name of the file, excluding the path leading to it (readonly)
+ * {DOMString} fullPath the absolute full path to the file (readonly)
+ * {FileSystem} filesystem on which the file resides (readonly)
+ */
+var FileEntry = function(name, fullPath) {
+     FileEntry.__super__.constructor.apply(this, [true, false, name, fullPath]);
+};
+
+utils.extend(FileEntry, Entry);
+
+/**
+ * Creates a new FileWriter associated with the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new FileWriter
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.createWriter = function(successCallback, errorCallback) {
+    this.file(function(filePointer) {
+        var writer = new FileWriter(filePointer);
+
+        if (writer.fileName === null || writer.fileName === "") {
+            if (typeof errorCallback === "function") {
+                errorCallback(new FileError(FileError.INVALID_STATE_ERR));
+            }
+        } else {
+            if (typeof successCallback === "function") {
+                successCallback(writer);
+            }
+        }
+    }, errorCallback);
+};
+
+/**
+ * Returns a File that represents the current state of the file that this FileEntry represents.
+ *
+ * @param {Function} successCallback is called with the new File object
+ * @param {Function} errorCallback is called with a FileError
+ */
+FileEntry.prototype.file = function(successCallback, errorCallback) {
+    var win = typeof successCallback !== 'function' ? null : function(f) {
+        var file = new File(f.name, f.fullPath, f.type, f.lastModifiedDate, f.size);
+        successCallback(file);
+    };
+    var fail = typeof errorCallback !== 'function' ? null : function(code) {
+        errorCallback(new FileError(code));
+    };
+    exec(win, fail, "File", "getFileMetadata", [this.fullPath]);
+};
+
+
+module.exports = FileEntry;
+});
+
+// file: lib/common/plugin/FileError.js
+define("cordova/plugin/FileError", function(require, exports, module) {
+/**
+ * FileError
+ */
+function FileError(error) {
+  this.code = error || null;
+}
+
+// File error codes
+// Found in DOMException
+FileError.NOT_FOUND_ERR = 1;
+FileError.SECURITY_ERR = 2;
+FileError.ABORT_ERR = 3;
+
+// Added by File API specification
+FileError.NOT_READABLE_ERR = 4;
+FileError.ENCODING_ERR = 5;
+FileError.NO_MODIFICATION_ALLOWED_ERR = 6;
+FileError.INVALID_STATE_ERR = 7;
+FileError.SYNTAX_ERR = 8;
+FileError.INVALID_MODIFICATION_ERR = 9;
+FileError.QUOTA_EXCEEDED_ERR = 10;
+FileError.TYPE_MISMATCH_ERR = 11;
+FileError.PATH_EXISTS_ERR = 12;
+
+module.exports = FileError;
+});
+
+// file: lib/common/plugin/FileReader.js
+define("cordova/plugin/FileReader", function(require, exports, module) {
+var exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError'),
+    ProgressEvent = require('cordova/plugin/ProgressEvent');
+
+/**
+ * This class reads the mobile device file system.
+ *
+ * For Android:
+ *      The root directory is the root of the file system.
+ *      To read from the SD card, the file name is "sdcard/my_file.txt"
+ * @constructor
+ */
+var FileReader = function() {
+    this.fileName = "";
+
+    this.readyState = 0; // FileReader.EMPTY
+
+    // File data
+    this.result = null;
+
+    // Error
+    this.error = null;
+
+    // Event handlers
+    this.onloadstart = null;    // When the read starts.
+    this.onprogress = null;     // While reading (and decoding) file or fileBlob data, and reporting partial file data (progess.loaded/progress.total)
+    this.onload = null;         // When the read has successfully completed.
+    this.onerror = null;        // When the read has failed (see errors).
+    this.onloadend = null;      // When the request has completed (either in success or failure).
+    this.onabort = null;        // When the read has been aborted. For instance, by invoking the abort() method.
+};
+
+// States
+FileReader.EMPTY = 0;
+FileReader.LOADING = 1;
+FileReader.DONE = 2;
+
+/**
+ * Abort reading file.
+ */
+FileReader.prototype.abort = function() {
+    this.result = null;
+
+    if (this.readyState == FileReader.DONE || this.readyState == FileReader.EMPTY) {
+      return;
+    }
+
+    this.readyState = FileReader.DONE;
+
+    // If abort callback
+    if (typeof this.onabort === 'function') {
+        this.onabort(new ProgressEvent('abort', {target:this}));
+    }
+    // If load end callback
+    if (typeof this.onloadend === 'function') {
+        this.onloadend(new ProgressEvent('loadend', {target:this}));
+    }
+};
+
+/**
+ * Read text file.
+ *
+ * @param file          {File} File object containing file properties
+ * @param encoding      [Optional] (see http://www.iana.org/assignments/character-sets)
+ */
+FileReader.prototype.readAsText = function(file, encoding) {
+    // Figure out pathing
+    this.fileName = '';
+    if (typeof file.fullPath === 'undefined') {
+        this.fileName = file;
+    } else {
+        this.fileName = file.fullPath;
+    }
+
+    // Already loading something
+    if (this.readyState == FileReader.LOADING) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    // LOADING state
+    this.readyState = FileReader.LOADING;
+
+    // If loadstart callback
+    if (typeof this.onloadstart === "function") {
+        this.onloadstart(new ProgressEvent("loadstart", {target:this}));
+    }
+
+    // Default encoding is UTF-8
+    var enc = encoding ? encoding : "UTF-8";
+
+    var me = this;
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileReader.DONE) {
+                return;
+            }
+
+            // Save result
+            me.result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // DONE state
+            me.readyState = FileReader.DONE;
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me.readyState = FileReader.DONE;
+
+            // null result
+            me.result = null;
+
+            // Save error
+            me.error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsText", [this.fileName, enc]);
+};
+
+
+/**
+ * Read file and return data as a base64 encoded data url.
+ * A data url is of the form:
+ *      data:[<mediatype>][;base64],<data>
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsDataURL = function(file) {
+    this.fileName = "";
+    if (typeof file.fullPath === "undefined") {
+        this.fileName = file;
+    } else {
+        this.fileName = file.fullPath;
+    }
+
+    // Already loading something
+    if (this.readyState == FileReader.LOADING) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    // LOADING state
+    this.readyState = FileReader.LOADING;
+
+    // If loadstart callback
+    if (typeof this.onloadstart === "function") {
+        this.onloadstart(new ProgressEvent("loadstart", {target:this}));
+    }
+
+    var me = this;
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me.readyState = FileReader.DONE;
+
+            // Save result
+            me.result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me.readyState = FileReader.DONE;
+
+            me.result = null;
+
+            // Save error
+            me.error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsDataURL", [this.fileName]);
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsBinaryString = function(file) {
+    // TODO - Can't return binary data to browser.
+    console.log('method "readAsBinaryString" is not supported at this time.');
+};
+
+/**
+ * Read file and return data as a binary data.
+ *
+ * @param file          {File} File object containing file properties
+ */
+FileReader.prototype.readAsArrayBuffer = function(file) {
+    // TODO - Can't return binary data to browser.
+    console.log('This method is not supported at this time.');
+};
+
+module.exports = FileReader;
+});
+
+// file: lib/common/plugin/FileSystem.js
+define("cordova/plugin/FileSystem", function(require, exports, module) {
+var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
+
+/**
+ * An interface representing a file system
+ *
+ * @constructor
+ * {DOMString} name the unique name of the file system (readonly)
+ * {DirectoryEntry} root directory of the file system (readonly)
+ */
+var FileSystem = function(name, root) {
+    this.name = name || null;
+    if (root) {
+        this.root = new DirectoryEntry(root.name, root.fullPath);
+    }
+};
+
+module.exports = FileSystem;
+
+});
+
+// file: lib/common/plugin/FileTransfer.js
+define("cordova/plugin/FileTransfer", function(require, exports, module) {
+var exec = require('cordova/exec'),
+    FileTransferError = require('cordova/plugin/FileTransferError');
+
+/**
+ * FileTransfer uploads a file to a remote server.
+ * @constructor
+ */
+var FileTransfer = function() {};
+
+/**
+* Given an absolute file path, uploads a file on the device to a remote server
+* using a multipart HTTP request.
+* @param filePath {String}           Full path of the file on the device
+* @param server {String}             URL of the server to receive the file
+* @param successCallback (Function}  Callback to be invoked when upload has completed
+* @param errorCallback {Function}    Callback to be invoked upon error
+* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
+* @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
+*/
+FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, trustAllHosts) {
+    // sanity parameter checking
+    if (!filePath || !server) throw new Error("FileTransfer.upload requires filePath and server URL parameters at the minimum.");
+    // check for options
+    var fileKey = null;
+    var fileName = null;
+    var mimeType = null;
+    var params = null;
+    var chunkedMode = true;
+    if (options) {
+        fileKey = options.fileKey;
+        fileName = options.fileName;
+        mimeType = options.mimeType;
+        if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
+            chunkedMode = options.chunkedMode;
+        }
+        if (options.params) {
+            params = options.params;
+        }
+        else {
+            params = {};
+        }
+    }
+
+    var fail = function(e) {
+        var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
+        errorCallback(error);
+    };
+
+    exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]);
+};
+
+/**
+ * Downloads a file form a given URL and saves it to the specified directory.
+ * @param source {String}          URL of the server to receive the file
+ * @param target {String}         Full path of the file on the device
+ * @param successCallback (Function}  Callback to be invoked when upload has completed
+ * @param errorCallback {Function}    Callback to be invoked upon error
+ */
+FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) {
+    // sanity parameter checking
+    if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum.");
+    var win = function(result) {
+        var entry = null;
+        if (result.isDirectory) {
+            entry = new (require('cordova/plugin/DirectoryEntry'))();
+        }
+        else if (result.isFile) {
+            entry = new (require('cordova/plugin/FileEntry'))();
+        }
+        entry.isDirectory = result.isDirectory;
+        entry.isFile = result.isFile;
+        entry.name = result.name;
+        entry.fullPath = result.fullPath;
+        successCallback(entry);
+    };
+
+    var fail = function(e) {
+        var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
+        errorCallback(error);
+    };
+
+    exec(win, errorCallback, 'FileTransfer', 'download', [source, target]);
+};
+
+module.exports = FileTransfer;
+
+});
+
+// file: lib/common/plugin/FileTransferError.js
+define("cordova/plugin/FileTransferError", function(require, exports, module) {
+/**
+ * FileTransferError
+ * @constructor
+ */
+var FileTransferError = function(code, source, target, status) {
+    this.code = code || null;
+    this.source = source || null;
+    this.target = target || null;
+    this.http_status = status || null;
+};
+
+FileTransferError.FILE_NOT_FOUND_ERR = 1;
+FileTransferError.INVALID_URL_ERR = 2;
+FileTransferError.CONNECTION_ERR = 3;
+
+module.exports = FileTransferError;
+
+});
+
+// file: lib/common/plugin/FileUploadOptions.js
+define("cordova/plugin/FileUploadOptions", function(require, exports, module) {
+/**
+ * Options to customize the HTTP request used to upload files.
+ * @constructor
+ * @param fileKey {String}   Name of file request parameter.
+ * @param fileName {String}  Filename to be used by the server. Defaults to image.jpg.
+ * @param mimeType {String}  Mimetype of the uploaded file. Defaults to image/jpeg.
+ * @param params {Object}    Object with key: value params to send to the server.
+ */
+var FileUploadOptions = function(fileKey, fileName, mimeType, params) {
+    this.fileKey = fileKey || null;
+    this.fileName = fileName || null;
+    this.mimeType = mimeType || null;
+    this.params = params || null;
+};
+
+module.exports = FileUploadOptions;
+});
+
+// file: lib/common/plugin/FileUploadResult.js
+define("cordova/plugin/FileUploadResult", function(require, exports, module) {
+/**
+ * FileUploadResult
+ * @constructor
+ */
+var FileUploadResult = function() {
+    this.bytesSent = 0;
+    this.responseCode = null;
+    this.response = null;
+};
+
+module.exports = FileUploadResult;
+});
+
+// file: lib/common/plugin/FileWriter.js
+define("cordova/plugin/FileWriter", function(require, exports, module) {
+var exec = require('cordova/exec'),
+    FileError = require('cordova/plugin/FileError'),
+    ProgressEvent = require('cordova/plugin/ProgressEvent');
+
+/**
+ * This class writes to the mobile device file system.
+ *
+ * For Android:
+ *      The root directory is the root of the file system.
+ *      To write to the SD card, the file name is "sdcard/my_file.txt"
+ *
+ * @constructor
+ * @param file {File} File object containing file properties
+ * @param append if true write to the end of the file, otherwise overwrite the file
+ */
+var FileWriter = function(file) {
+    this.fileName = "";
+    this.length = 0;
+    if (file) {
+        this.fileName = file.fullPath || file;
+        this.length = file.size || 0;
+    }
+    // default is to write at the beginning of the file
+    this.position = 0;
+
+    this.readyState = 0; // EMPTY
+
+    this.result = null;
+
+    // Error
+    this.error = null;
+
+    // Event handlers
+    this.onwritestart = null;   // When writing starts
+    this.onprogress = null;     // While writing the file, and reporting partial file data
+    this.onwrite = null;        // When the write has successfully completed.
+    this.onwriteend = null;     // When the request has completed (either in success or failure).
+    this.onabort = null;        // When the write has been aborted. For instance, by invoking the abort() method.
+    this.onerror = null;        // When the write has failed (see errors).
+};
+
+// States
+FileWriter.INIT = 0;
+FileWriter.WRITING = 1;
+FileWriter.DONE = 2;
+
+/**
+ * Abort writing file.
+ */
+FileWriter.prototype.abort = function() {
+    // check for invalid state
+    if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    // set error
+    this.error = new FileError(FileError.ABORT_ERR);
+
+    this.readyState = FileWriter.DONE;
+
+    // If abort callback
+    if (typeof this.onabort === "function") {
+        this.onabort(new ProgressEvent("abort", {"target":this}));
+    }
+
+    // If write end callback
+    if (typeof this.onwriteend === "function") {
+        this.onwriteend(new ProgressEvent("writeend", {"target":this}));
+    }
+};
+
+/**
+ * Writes data to the file
+ *
+ * @param text to be written
+ */
+FileWriter.prototype.write = function(text) {
+    // Throw an exception if we are already writing a file
+    if (this.readyState === FileWriter.WRITING) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    // WRITING state
+    this.readyState = FileWriter.WRITING;
+
+    var me = this;
+
+    // If onwritestart callback
+    if (typeof me.onwritestart === "function") {
+        me.onwritestart(new ProgressEvent("writestart", {"target":me}));
+    }
+
+    // Write file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // position always increases by bytes written because file would be extended
+            me.position += r;
+            // The length of the file is now where we are done writing.
+
+            me.length = me.position;
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // If onwrite callback
+            if (typeof me.onwrite === "function") {
+                me.onwrite(new ProgressEvent("write", {"target":me}));
+            }
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // Save error
+            me.error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {"target":me}));
+            }
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+            }
+        }, "File", "write", [this.fileName, text, this.position]);
+};
+
+/**
+ * Moves the file pointer to the location specified.
+ *
+ * If the offset is a negative number the position of the file
+ * pointer is rewound.  If the offset is greater than the file
+ * size the position is set to the end of the file.
+ *
+ * @param offset is the location to move the file pointer to.
+ */
+FileWriter.prototype.seek = function(offset) {
+    // Throw an exception if we are already writing a file
+    if (this.readyState === FileWriter.WRITING) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    if (!offset && offset !== 0) {
+        return;
+    }
+
+    // See back from end of file.
+    if (offset < 0) {
+        this.position = Math.max(offset + this.length, 0);
+    }
+    // Offset is bigger then file size so set position
+    // to the end of the file.
+    else if (offset > this.length) {
+        this.position = this.length;
+    }
+    // Offset is between 0 and file size so set the position
+    // to start writing.
+    else {
+        this.position = offset;
+    }
+};
+
+/**
+ * Truncates the file to the size specified.
+ *
+ * @param size to chop the file at.
+ */
+FileWriter.prototype.truncate = function(size) {
+    // Throw an exception if we are already writing a file
+    if (this.readyState === FileWriter.WRITING) {
+        throw new FileError(FileError.INVALID_STATE_ERR);
+    }
+
+    // WRITING state
+    this.readyState = FileWriter.WRITING;
+
+    var me = this;
+
+    // If onwritestart callback
+    if (typeof me.onwritestart === "function") {
+        me.onwritestart(new ProgressEvent("writestart", {"target":this}));
+    }
+
+    // Write file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // Update the length of the file
+            me.length = r;
+            me.position = Math.min(me.position, r);
+
+            // If onwrite callback
+            if (typeof me.onwrite === "function") {
+                me.onwrite(new ProgressEvent("write", {"target":me}));
+            }
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me.readyState === FileWriter.DONE) {
+                return;
+            }
+
+            // DONE state
+            me.readyState = FileWriter.DONE;
+
+            // Save error
+            me.error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {"target":me}));
+            }
+
+            // If onwriteend callback
+            if (typeof me.onwriteend === "function") {
+                me.onwriteend(new ProgressEvent("writeend", {"target":me}));
+            }
+        }, "File", "truncate", [this.fileName, size]);
+};
+
+module.exports = FileWriter;
+
+});
+
+// file: lib/common/plugin/Flags.js
+define("cordova/plugin/Flags", function(require, exports, module) {
+/**
+ * Supplies arguments to methods that lookup or create files and directories.
+ *
+ * @param create
+ *            {boolean} file or directory if it doesn't exist
+ * @param exclusive
+ *            {boolean} used with create; if true the command will fail if
+ *            target path exists
+ */
+function Flags(create, exclusive) {
+    this.create = create || false;
+    this.exclusive = exclusive || false;
+}
+
+module.exports = Flags;
+});
+
+// file: lib/common/plugin/LocalFileSystem.js
+define("cordova/plugin/LocalFileSystem", function(require, exports, module) {
+var exec = require('cordova/exec');
+
+/**
+ * Represents a local file system.
+ */
+var LocalFileSystem = function() {
+
+};
+
+LocalFileSystem.TEMPORARY = 0; //temporary, with no guarantee of persistence
+LocalFileSystem.PERSISTENT = 1; //persistent
+
+module.exports = LocalFileSystem;
+});
+
+// file: lib/common/plugin/Media.js
+define("cordova/plugin/Media", function(require, exports, module) {
+var utils = require('cordova/utils'),
+    exec = require('cordova/exec');
+
+var mediaObjects = {};
+
+/**
+ * This class provides access to the device media, interfaces to both sound and video
+ *
+ * @constructor
+ * @param src                   The file name or url to play
+ * @param successCallback       The callback to be called when the file is done playing or recording.
+ *                                  successCallback()
+ * @param errorCallback         The callback to be called if there is an error.
+ *                                  errorCallback(int errorCode) - OPTIONAL
+ * @param statusCallback        The callback to be called when media status has changed.
+ *                                  statusCallback(int statusCode) - OPTIONAL
+ */
+var Media = function(src, successCallback, errorCallback, statusCallback) {
+
+    // successCallback optional
+    if (successCallback && (typeof successCallback !== "function")) {
+        console.log("Media Error: successCallback is not a function");
+        return;
+    }
+
+    // errorCallback optional
+    if (errorCallback && (typeof errorCallback !== "function")) {
+        console.log("Media Error: errorCallback is not a function");
+        return;
+    }
+
+    // statusCallback optional
+    if (statusCallback && (typeof statusCallback !== "function")) {
+        console.log("Media Error: statusCallback is not a function");
+        return;
+    }
+
+    this.id = utils.createUUID();
+    mediaObjects[this.id] = this;
+    this.src = src;
+    this.successCallback = successCallback;
+    this.errorCallback = errorCallback;
+    this.statusCallback = statusCallback;
+    this._duration = -1;
+    this._position = -1;
+    exec(null, this.errorCallback, "Media", "create", [this.id, this.src]);
+};
+
+// Media messages
+Media.MEDIA_STATE = 1;
+Media.MEDIA_DURATION = 2;
+Media.MEDIA_POSITION = 3;
+Media.MEDIA_ERROR = 9;
+
+// Media states
+Media.MEDIA_NONE = 0;
+Media.MEDIA_STARTING = 1;
+Media.MEDIA_RUNNING = 2;
+Media.MEDIA_PAUSED = 3;
+Media.MEDIA_STOPPED = 4;
+Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
+
+// "static" function to return existing objs.
+Media.get = function(id) {
+    return mediaObjects[id];
+};
+
+/**
+ * Start or resume playing audio file.
+ */
+Media.prototype.play = function(options) {
+    exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]);
+};
+
+/**
+ * Stop playing audio file.
+ */
+Media.prototype.stop = function() {
+    var me = this;
+    exec(function() {
+        me._position = 0;
+        me.successCallback();
+    }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]);
+};
+
+/**
+ * Seek or jump to a new time in the track..
+ */
+Media.prototype.seekTo = function(milliseconds) {
+    var me = this;
+    exec(function(p) {
+        me._position = p;
+    }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]);
+};
+
+/**
+ * Pause playing audio file.
+ */
+Media.prototype.pause = function() {
+    exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]);
+};
+
+/**
+ * Get duration of an audio file.
+ * The duration is only set for audio that is playing, paused or stopped.
+ *
+ * @return      duration or -1 if not known.
+ */
+Media.prototype.getDuration = function() {
+    return this._duration;
+};
+
+/**
+ * Get position of audio.
+ */
+Media.prototype.getCurrentPosition = function(success, fail) {
+    var me = this;
+    exec(function(p) {
+        me._position = p;
+        success(p);
+    }, fail, "Media", "getCurrentPositionAudio", [this.id]);
+};
+
+/**
+ * Start recording audio file.
+ */
+Media.prototype.startRecord = function() {
+    exec(this.successCallback, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]);
+};
+
+/**
+ * Stop recording audio file.
+ */
+Media.prototype.stopRecord = function() {
+    exec(this.successCallback, this.errorCallback, "Media", "stopRecordingAudio", [this.id]);
+};
+
+/**
+ * Release the resources.
+ */
+Media.prototype.release = function() {
+    exec(null, this.errorCallback, "Media", "release", [this.id]);
+};
+
+/**
+ * Adjust the volume.
+ */
+Media.prototype.setVolume = function(volume) {
+    exec(null, null, "Media", "setVolume", [this.id, volume]);
+};
+
+/**
+ * Audio has status update.
+ * PRIVATE
+ *
+ * @param id            The media object id (string)
+ * @param status        The status code (int)
+ * @param msg           The status message (string)
+ */
+Media.onStatus = function(id, msg, value) {
+    var media = mediaObjects[id];
+    // If state update
+    if (msg === Media.MEDIA_STATE) {
+        if (value === Media.MEDIA_STOPPED) {
+            if (media.successCallback) {
+                media.successCallback();
+            }
+        }
+        if (media.statusCallback) {
+            media.statusCallback(value);
+        }
+    }
+    else if (msg === Media.MEDIA_DURATION) {
+        media._duration = value;
+    }
+    else if (msg === Media.MEDIA_ERROR) {
+        if (media.errorCallback) {
+            // value should be a MediaError object when msg == MEDIA_ERROR
+            media.errorCallback(value);
+        }
+    }
+    else if (msg === Media.MEDIA_POSITION) {
+        media._position = value;
+    }
+};
+
+module.exports = Media;
+});
+
+// file: lib/common/plugin/MediaError.js
+define("cordova/plugin/MediaError", function(require, exports, module) {
+/**
+ * This class contains information about any Media errors.
+ * @constructor
+ */
+var MediaError = function(code, msg) {
+    this.code = (code !== undefined ? code : null);
+    this.message = msg || "";
+};
+
+MediaError.MEDIA_ERR_NONE_ACTIVE    = 0;
+MediaError.MEDIA_ERR_ABORTED        = 1;
+MediaError.MEDIA_ERR_NETWORK        = 2;
+MediaError.MEDIA_ERR_DECODE         = 3;
+MediaError.MEDIA_ERR_NONE_SUPPORTED = 4;
+
+module.exports = MediaError;
+});
+
+// file: lib/common/plugin/MediaFile.js
+define("cordova/plugin/MediaFile", function(require, exports, module) {
+var utils = require('cordova/utils'),
+    exec = require('cordova/exec'),
+    File = require('cordova/plugin/File'),
+    CaptureError = re