You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by st...@apache.org on 2013/06/18 02:53:58 UTC

[1/5] added docs + tests

Updated Branches:
  refs/heads/master 66ab05e7a -> acf384a66


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/cordova-incl.js
----------------------------------------------------------------------
diff --git a/test/cordova-incl.js b/test/cordova-incl.js
new file mode 100644
index 0000000..a82c590
--- /dev/null
+++ b/test/cordova-incl.js
@@ -0,0 +1,85 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+var PLAT;
+(function getPlatform() {
+    var platforms = {
+        android: /Android/,
+        ios: /(iPad)|(iPhone)|(iPod)/,
+        blackberry10: /(BB10)/,
+        blackberry: /(PlayBook)|(BlackBerry)/,
+        windows8: /MSAppHost/,
+        windowsphone: /Windows Phone/
+    };
+    for (var key in platforms) {
+        if (platforms[key].exec(navigator.userAgent)) {
+            PLAT = key;
+            break;
+        }
+    }
+})();
+
+var scripts = document.getElementsByTagName('script');
+var currentPath = scripts[scripts.length - 1].src;
+var platformCordovaPath = currentPath.replace("cordova-incl.js", "cordova." + PLAT + ".js");
+var normalCordovaPath = currentPath.replace("cordova-incl.js", "cordova.js");
+var cordovaPath = normalCordovaPath;
+
+if (PLAT) {
+    // XHR to local file is an error on some platforms, windowsphone for one 
+    try {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", platformCordovaPath, false);
+        xhr.onreadystatechange = function() {
+
+            if (this.readyState == this.DONE && this.responseText.length > 0) {
+                if(parseInt(this.status) >= 400){
+                    cordovaPath = normalCordovaPath;
+                }else{
+                    cordovaPath = platformCordovaPath;
+                }
+            }
+        };
+        xhr.send(null);
+    }
+    catch(e){
+        cordovaPath = normalCordovaPath;
+    } // access denied!
+}
+
+if (!window._doNotWriteCordovaScript) {
+    if (PLAT != "windows8") {
+        document.write('<script type="text/javascript" charset="utf-8" src="' + cordovaPath + '"></script>');
+    } else {
+        var s = document.createElement('script');
+        s.src = cordovaPath;
+        document.head.appendChild(s);
+    }
+}
+
+function backHome() {
+    if (window.device && device.platform && device.platform.toLowerCase() == 'android') {
+        navigator.app.backHistory();
+    }
+    else {
+        window.history.go(-1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/index.html
----------------------------------------------------------------------
diff --git a/test/index.html b/test/index.html
new file mode 100644
index 0000000..727c504
--- /dev/null
+++ b/test/index.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<!--
+
+ 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.
+
+-->
+
+
+<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">
+    <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-incl.js"></script>
+	  <script type="text/javascript" charset="utf-8" src="main.js"></script>
+
+  </head>
+  <body onload="init();" id="stage" class="theme">
+    <h1>Apache Cordova Tests</h1>
+    <div id="info">
+        <h4>Platform: <span id="platform">  </span></h4>
+        <h4>Version: <span id="version"> </span></h4>
+        <h4>UUID: <span id="uuid">  </span></h4>
+        <h4>Model: <span id="model"> </span></h4>
+        <h4>Width: <span id="width">  </span>,   Height: <span id="height"> 
+                   </span>, Color Depth: <span id="colorDepth"></span></h4>
+        <h4>User-Agent: <span id="user-agent"> </span></h4>
+     </div>
+    <a href="autotest/index.html" class="btn large">Automatic Test</a>
+    <a href="accelerometer/index.html" class="btn large">Accelerometer</a>
+    <a href="audio/index.html" class="btn large">Audio Play/Record</a>
+    <a href="battery/index.html" class="btn large">Battery</a>
+    <a href="camera/index.html" class="btn large">Camera</a>
+    <a href="compass/index.html" class="btn large">Compass</a>
+    <a href="contacts/index.html" class="btn large">Contacts</a>
+    <a href="events/index.html" class="btn large">Events</a>
+    <a href="location/index.html" class="btn large">Location</a>
+    <a href="lazyloadjs/index.html" class="btn large">Lazy Loading of cordova-incl.js</a>
+    <a href="misc/index.html" class="btn large">Misc Content</a>
+    <a href="network/index.html" class="btn large">Network</a>
+    <a href="notification/index.html" class="btn large">Notification</a>
+    <a href="splashscreen/index.html" class="btn large">Splashscreen</a>
+    <a href="sql/index.html" class="btn large">Web SQL</a>
+    <a href="storage/index.html" class="btn large">Local Storage</a>
+    <a href="benchmarks/index.html" class="btn large">Benchmarks</a>
+    <a href="inappbrowser/index.html" class="btn large">In App Browser</a>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/main.js
----------------------------------------------------------------------
diff --git a/test/main.js b/test/main.js
new file mode 100644
index 0000000..5f071c4
--- /dev/null
+++ b/test/main.js
@@ -0,0 +1,162 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+var deviceInfo = function() {
+    document.getElementById("platform").innerHTML = device.platform;
+    document.getElementById("version").innerHTML = device.version;
+    document.getElementById("uuid").innerHTML = device.uuid;
+    document.getElementById("model").innerHTML = device.model;
+    document.getElementById("width").innerHTML = screen.width;
+    document.getElementById("height").innerHTML = screen.height;
+    document.getElementById("colorDepth").innerHTML = screen.colorDepth;
+};
+
+var getLocation = function() {
+    var suc = function(p) {
+        alert(p.coords.latitude + " " + p.coords.longitude);
+    };
+    var locFail = function() {
+    };
+    navigator.geolocation.getCurrentPosition(suc, locFail);
+};
+
+var beep = function() {
+    navigator.notification.beep(2);
+};
+
+var vibrate = function() {
+    navigator.notification.vibrate(0);
+};
+
+function roundNumber(num) {
+    var dec = 3;
+    var result = Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
+    return result;
+}
+
+var accelerationWatch = null;
+
+function updateAcceleration(a) {
+    document.getElementById('x').innerHTML = roundNumber(a.x);
+    document.getElementById('y').innerHTML = roundNumber(a.y);
+    document.getElementById('z').innerHTML = roundNumber(a.z);
+}
+
+var toggleAccel = function() {
+    if (accelerationWatch !== null) {
+        navigator.accelerometer.clearWatch(accelerationWatch);
+        updateAcceleration({
+            x : "",
+            y : "",
+            z : ""
+        });
+        accelerationWatch = null;
+    } else {
+        var options = {};
+        options.frequency = 1000;
+        accelerationWatch = navigator.accelerometer.watchAcceleration(
+                updateAcceleration, function(ex) {
+                    alert("accel fail (" + ex.name + ": " + ex.message + ")");
+                }, options);
+    }
+};
+
+var preventBehavior = function(e) {
+    e.preventDefault();
+};
+
+function dump_pic(data) {
+    var viewport = document.getElementById('viewport');
+    console.log(data);
+    viewport.style.display = "";
+    viewport.style.position = "absolute";
+    viewport.style.top = "10px";
+    viewport.style.left = "10px";
+    document.getElementById("test_img").src = "data:image/jpeg;base64," + data;
+}
+
+function fail(msg) {
+    alert(msg);
+}
+
+function show_pic() {
+    navigator.camera.getPicture(dump_pic, fail, {
+        quality : 50
+    });
+}
+
+function close() {
+    var viewport = document.getElementById('viewport');
+    viewport.style.position = "relative";
+    viewport.style.display = "none";
+}
+
+// This is just to do this.
+function readFile() {
+    navigator.file.read('/sdcard/cordova.txt', fail, fail);
+}
+
+function writeFile() {
+    navigator.file.write('foo.txt', "This is a test of writing to a file",
+            fail, fail);
+}
+
+function contacts_success(contacts) {
+    alert(contacts.length
+            + ' contacts returned.'
+            + (contacts[2] && contacts[2].name ? (' Third contact is ' + contacts[2].name.formatted)
+                    : ''));
+}
+
+function get_contacts() {
+    var obj = new ContactFindOptions();
+    obj.filter = "";
+    obj.multiple = true;
+    obj.limit = 5;
+    navigator.service.contacts.find(
+            [ "displayName", "name" ], contacts_success,
+            fail, obj);
+}
+
+var networkReachableCallback = function(reachability) {
+    // There is no consistency on the format of reachability
+    var networkState = reachability.code || reachability;
+
+    var currentState = {};
+    currentState[NetworkStatus.NOT_REACHABLE] = 'No network connection';
+    currentState[NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK] = 'Carrier data connection';
+    currentState[NetworkStatus.REACHABLE_VIA_WIFI_NETWORK] = 'WiFi connection';
+
+    confirm("Connection type:\n" + currentState[networkState]);
+};
+
+function check_network() {
+    navigator.network.isReachable("www.mobiledevelopersolutions.com",
+            networkReachableCallback, {});
+}
+
+function init() {
+    // the next line makes it impossible to see Contacts on the HTC Evo since it
+    // doesn't have a scroll button
+    // document.addEventListener("touchmove", preventBehavior, false);
+    document.addEventListener("deviceready", deviceInfo, true);
+    document.getElementById("user-agent").textContent = navigator.userAgent;
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/master.css
----------------------------------------------------------------------
diff --git a/test/master.css b/test/master.css
new file mode 100644
index 0000000..e93c937
--- /dev/null
+++ b/test/master.css
@@ -0,0 +1,164 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+  body {
+    background:#222 none repeat scroll 0 0;
+    color:#666;
+    font-family:Helvetica;
+    font-size:72%;
+    line-height:1.5em;
+    margin:0;
+    border-top:1px solid #393939;
+  }
+
+  #info{
+    background:#ffa;
+    border: 1px solid #ffd324;
+    -webkit-border-radius: 5px;
+    border-radius: 5px;
+    clear:both;
+    margin:15px 6px 0;
+    min-width:295px;
+    max-width:97%;
+    padding:4px 0px 2px 10px;
+    word-wrap:break-word;
+    margin-bottom:10px;
+    display:inline-block;
+    min-height: 160px;
+    max-height: 300px;
+    overflow: auto;
+    -webkit-overflow-scrolling: touch;
+  }
+  
+  #info > h4{
+    font-size:.95em;
+    margin:5px 0;
+  }
+ 	
+  #stage.theme{
+    padding-top:3px;
+  }
+
+  /* Definition List */
+  #stage.theme > dl{
+  	padding-top:10px;
+  	clear:both;
+  	margin:0;
+  	list-style-type:none;
+  	padding-left:10px;
+  	overflow:auto;
+  }
+
+  #stage.theme > dl > dt{
+  	font-weight:bold;
+  	float:left;
+  	margin-left:5px;
+  }
+
+  #stage.theme > dl > dd{
+  	width:45px;
+  	float:left;
+  	color:#a87;
+  	font-weight:bold;
+  }
+
+  /* Content Styling */
+  #stage.theme > h1, #stage.theme > h2, #stage.theme > p{
+    margin:1em 0 .5em 13px;
+  }
+
+  #stage.theme > h1{
+    color:#eee;
+    font-size:1.6em;
+    text-align:center;
+    margin:0;
+    margin-top:15px;
+    padding:0;
+  }
+
+  #stage.theme > h2{
+  	clear:both;
+    margin:0;
+    padding:3px;
+    font-size:1em;
+    text-align:center;
+  }
+
+  /* Stage Buttons */
+  #stage.theme .btn{
+  	border: 1px solid #555;
+  	-webkit-border-radius: 5px;
+  	border-radius: 5px;
+  	text-align:center;
+  	display:inline-block;
+  	background:#444;
+  	width:150px;
+  	color:#9ab;
+  	font-size:1.1em;
+  	text-decoration:none;
+  	padding:1.2em 0;
+  	margin:3px 0px 3px 5px;
+  }
+  
+  #stage.theme .large{
+  	width:308px;
+  	padding:1.2em 0;
+  }
+  
+  #stage.theme .wide{
+    width:100%;
+    padding:1.2em 0;
+  }
+  
+  #stage.theme .backBtn{
+   border: 1px solid #555;
+   -webkit-border-radius: 5px;
+   border-radius: 5px;
+   text-align:center;
+   display:block;
+   float:right;
+   background:#666;
+   width:75px;
+   color:#9ab;
+   font-size:1.1em;
+   text-decoration:none;
+   padding:1.2em 0;
+   margin:3px 5px 3px 5px;
+  }
+  
+  #stage.theme .input{
+   border: 1px solid #555;
+   -webkit-border-radius: 5px;
+   border-radius: 5px;
+   text-align:center;
+   display:block;
+   float:light;
+   background:#888;
+   color:#9cd;
+   font-size:1.1em;
+   text-decoration:none;
+   padding:1.2em 0;
+   margin:3px 0px 3px 5px;    
+ }
+  
+  #stage.theme .numeric{
+   width:100%;
+  }


[3/5] added docs + tests

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/pages/file.html
----------------------------------------------------------------------
diff --git a/test/autotest/pages/file.html b/test/autotest/pages/file.html
new file mode 100644
index 0000000..d143c74
--- /dev/null
+++ b/test/autotest/pages/file.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<!--
+
+ 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.
+
+-->
+
+
+<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-incl.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>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/test-runner.js
----------------------------------------------------------------------
diff --git a/test/autotest/test-runner.js b/test/autotest/test-runner.js
new file mode 100644
index 0000000..f72b3cc
--- /dev/null
+++ b/test/autotest/test-runner.js
@@ -0,0 +1,62 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+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;
+
+// Creates a spy that will fail if called.
+function createDoNotCallSpy(name, opt_extraMessage) {
+    return jasmine.createSpy().andCallFake(function() {
+        var errorMessage = name + ' should not have been called.';
+        if (arguments.length) {
+            errorMessage += ' Got args: ' + JSON.stringify(arguments);
+        }
+        if (opt_extraMessage) {
+            errorMessage += '\n' + opt_extraMessage;
+        }
+        expect(false).toBe(true, errorMessage);
+    });
+}
+
+// Waits for any of the given spys to be called.
+// Last param may be a custom timeout duration.
+function waitsForAny() {
+    var spys = [].slice.call(arguments);
+    var timeout = Tests.TEST_TIMEOUT;
+    if (typeof spys[spys.length - 1] == 'number') {
+        timeout = spys.pop();
+    }
+    waitsFor(function() {
+        for (var i = 0; i < spys.length; ++i) {
+            if (spys[i].wasCalled) {
+                return true;
+            }
+        }
+        return false;
+    }, "Expecting callbacks to be called.", timeout);
+}

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/tests/.DS_Store
----------------------------------------------------------------------
diff --git a/test/autotest/tests/.DS_Store b/test/autotest/tests/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/test/autotest/tests/.DS_Store differ


[5/5] git commit: added docs + tests

Posted by st...@apache.org.
added docs + tests


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/commit/acf384a6
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/tree/acf384a6
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/diff/acf384a6

Branch: refs/heads/master
Commit: acf384a665e8b0c00fced82c66b975dee9eb072e
Parents: 66ab05e
Author: Steven Gill <st...@gmail.com>
Authored: Mon Jun 17 17:53:54 2013 -0700
Committer: Steven Gill <st...@gmail.com>
Committed: Mon Jun 17 17:53:54 2013 -0700

----------------------------------------------------------------------
 docs/directoryentry/directoryentry.md       |  390 +++
 docs/directoryreader/directoryreader.md     |   69 +
 docs/file.md                                |   91 +
 docs/fileentry/fileentry.md                 |  323 ++
 docs/fileerror/fileerror.md                 |   51 +
 docs/fileobj/fileobj.md                     |   83 +
 docs/filereader/filereader.md               |  259 ++
 docs/filesystem/filesystem.md               |   95 +
 docs/fileuploadoptions/fileuploadoptions.md |   47 +
 docs/fileuploadresult/fileuploadresult.md   |   42 +
 docs/filewriter/filewriter.md               |  233 ++
 docs/flags/flags.md                         |   49 +
 docs/localfilesystem/localfilesystem.md     |  110 +
 docs/metadata/metadata.md                   |   54 +
 plugin.xml                                  |    3 +-
 test/autotest/html/HtmlReporter.js          |  101 +
 test/autotest/html/HtmlReporterHelpers.js   |   60 +
 test/autotest/html/ReporterView.js          |  164 +
 test/autotest/html/SpecView.js              |   79 +
 test/autotest/html/SuiteView.js             |   22 +
 test/autotest/html/TrivialReporter.js       |  192 ++
 test/autotest/index.html                    |   59 +
 test/autotest/jasmine.css                   |   81 +
 test/autotest/jasmine.js                    | 2530 +++++++++++++++
 test/autotest/pages/.DS_Store               |  Bin 0 -> 6148 bytes
 test/autotest/pages/file.html               |   90 +
 test/autotest/test-runner.js                |   62 +
 test/autotest/tests/.DS_Store               |  Bin 0 -> 6148 bytes
 test/autotest/tests/file.tests.js           | 3574 ++++++++++++++++++++++
 test/cordova-incl.js                        |   85 +
 test/index.html                             |   64 +
 test/main.js                                |  162 +
 test/master.css                             |  164 +
 33 files changed, 9386 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/directoryentry/directoryentry.md
----------------------------------------------------------------------
diff --git a/docs/directoryentry/directoryentry.md b/docs/directoryentry/directoryentry.md
new file mode 100644
index 0000000..de01dd5
--- /dev/null
+++ b/docs/directoryentry/directoryentry.md
@@ -0,0 +1,390 @@
+---
+license: 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.
+---
+
+DirectoryEntry
+==============
+
+This object represents a directory on a file system, as defined by the
+[W3C Directories and Systems](http://www.w3.org/TR/file-system-api/)
+specification.
+
+Properties
+----------
+
+- __isFile__: Always false. _(boolean)_
+- __isDirectory__: Always true. _(boolean)_
+- __name__: The name of the `DirectoryEntry`, excluding the path leading to it. _(DOMString)_
+- __fullPath__: The full absolute path from the root to the `DirectoryEntry`. _(DOMString)_
+
+__NOTE:__ The following attribute is defined by the W3C specification,
+but is _not_ supported:
+
+- __filesystem__: The file system on which the `DirectoryEntry` resides. _(FileSystem)_
+
+Methods
+-------
+
+The following methods can be invoked on a `DirectoryEntry` object:
+
+- __getMetadata__: Look up metadata about a directory.
+- __setMetadata__: Set metadata on a directory.
+- __moveTo__: Move a directory to a different location on the file system.
+- __copyTo__: Copy a directory to a different location on the file system.
+- __toURL__: Return a URL to help locate a directory.
+- __remove__: Delete a directory. The directory must be empty.
+- __getParent__: Look up the parent directory.
+- __createReader__: Create a new `DirectoryReader` that can read entries from a directory.
+- __getDirectory__: Create or look up a directory.
+- __getFile__: Create or look up a file.
+- __removeRecursively__: Delete a directory and all of its contents.
+
+Supported Platforms
+-------------------
+
+- Android
+- BlackBerry WebWorks (OS 5.0 and higher)
+- iOS
+- Windows Phone 7 and 8
+- Windows 8
+
+getMetadata
+-----------
+
+Look up metadata about a directory.
+
+__Parameters:__
+
+- __successCallback__: A callback function to execute with a `Metadata` object. _(Function)_
+- __errorCallback__: A callback function to execute if an error occurs when retrieving the `Metadata`. Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(metadata) {
+        console.log("Last Modified: " + metadata.modificationTime);
+    }
+
+    function fail(error) {
+        alert(error.code);
+    }
+
+    // Request the metadata object for this entry
+    entry.getMetadata(success, fail);
+
+setMetadata
+----------------
+
+Set metadata on a directory.
+__Currently works only on iOS.__ - this will set the extended attributes of a directory.
+
+__Parameters:__
+
+- __successCallback__: A callback that executes when the metadata is successfully set. _(Function)_
+- __errorCallback__: A callback that executes when the metadata fails to be set. _(Function)_
+- __metadataObject__: An object that contains the metadata's keys and values. _(Object)_
+
+__Quick Example__
+
+    function success() {
+        console.log("The metadata was successfully set.");
+    }
+
+    function fail() {
+        alert("There was an error in setting the metadata");
+    }
+
+    // Set the metadata
+    entry.setMetadata(success, fail, { "com.apple.MobileBackup": 1});
+
+__iOS Quirk__
+
+- Only the `com.apple.MobileBackup` extended attribute is supported. Set the value to `1` to prevent the directory from being backed up to iCloud. Set the value to `0` to re-enable the directory to be backed up to iCloud.
+
+__Quick Example__
+
+    function setFolderMetadata(localFileSystem, subFolder, metadataKey, metadataValue)
+    {
+            var onSetMetadataWin = function() {
+              console.log("success setting metadata")
+            }
+        var onSetMetadataFail = function() {
+              console.log("error setting metadata")
+        }
+
+            var onGetDirectoryWin = function(parent) {
+              var data = {};
+              data[metadataKey] = metadataValue;
+              parent.setMetadata(onSetMetadataWin, onSetMetadataFail, data);
+            }
+            var onGetDirectoryFail = function() {
+              console.log("error getting dir")
+            }
+
+            var onFSWin = function(fileSystem) {
+              fileSystem.root.getDirectory(subFolder, {create: true, exclusive: false}, onGetDirectoryWin, onGetDirectoryFail);
+            }
+
+            var onFSFail = function(evt) {
+                  console.log(evt.target.error.code);
+            }
+
+            window.requestFileSystem(localFileSystem, 0, onFSWin, onFSFail);
+    }
+
+        setFolderMetadata(LocalFileSystem.PERSISTENT, "Backups", "com.apple.MobileBackup", 1);
+
+moveTo
+------
+
+Move a directory to a different location on the file system. An error results if the app attempts to:
+
+- move a directory inside itself or to any child at any depth.
+- move a directory into its parent if a name different from its current directory is not provided.
+- move a directory to a path occupied by a file.
+- move a directory to a path occupied by a directory that is not empty.
+
+Moving a directory on top of an existing empty directory attempts to
+delete and replace that directory.
+
+__Parameters:__
+
+- __parent__: The parent directory to which to move the directory. _(DirectoryEntry)_
+- __newName__: The new name of the directory. Defaults to the current name if unspecified. _(DOMString)_
+- __successCallback__: A callback that executes with the `DirectoryEntry` object for the new directory. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when attempting to move the directory. Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(entry) {
+        console.log("New Path: " + entry.fullPath);
+    }
+
+    function fail(error) {
+        alert(error.code);
+    }
+
+        function moveDir(entry) {
+        var parent = document.getElementById('parent').value,
+            parentName = parent.substring(parent.lastIndexOf('/')+1),
+            newName = document.getElementById('newName').value,
+            parentEntry = new DirectoryEntry(parentName, parent);
+
+        // move the directory to a new directory and rename it
+        entry.moveTo(parentEntry, newName, success, fail);
+    }
+
+copyTo
+------
+
+Copy a directory to a different location on the file system.  An error results if the app attempts to:
+
+- copy a directory inside itself at any depth.
+- copy a directory into its parent if a name different from its current directory is not provided.
+
+Directory copies are always recursive, and copy all contents of the directory.
+
+__Parameters:__
+
+- __parent__: The parent directory to which to copy the directory. _(DirectoryEntry)_
+- __newName__: The new name of the directory. Defaults to the current name if unspecified. _(DOMString)_
+- __successCallback__: A callback that executes with the `DirectoryEntry` object for the new directory. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when attempting to copy the underlying directory. Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+        function win(entry) {
+            console.log("New Path: " + entry.fullPath);
+        }
+
+        function fail(error) {
+            alert(error.code);
+        }
+
+        function copyDir(entry) {
+        var parent = document.getElementById('parent').value,
+            parentName = parent.substring(parent.lastIndexOf('/')+1),
+            newName = document.getElementById('newName').value,
+            parentEntry = new DirectoryEntry(parentName, parent);
+
+        // copy the directory to a new directory and rename it
+        entry.copyTo(parentEntry, newName, success, fail);
+    }
+
+toURL
+-----
+
+Returns a URL that can be used to locate the directory.
+
+__Quick Example__
+
+    // Get the URL for this directory
+    var dirURL = entry.toURL();
+    console.log(dirURL);
+
+remove
+------
+
+Deletes a directory. An error results if the app attempts to:
+
+- delete a directory that is not empty.
+- delete the root directory of a filesystem.
+
+__Parameters:__
+
+- __successCallback__: A callback that executes after the directory is deleted.  Invoked with no parameters. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when attempting to delete the directory. Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(entry) {
+        console.log("Removal succeeded");
+    }
+
+    function fail(error) {
+        alert('Error removing directory: ' + error.code);
+    }
+
+    // remove this directory
+    entry.remove(success, fail);
+
+getParent
+---------
+
+Look up the parent `DirectoryEntry` containing the directory.
+
+__Parameters:__
+
+- __successCallback__: A callback that is passed the directory's parent `DirectoryEntry`. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when attempting to retrieve the parent `DirectoryEntry`. Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(parent) {
+        console.log("Parent Name: " + parent.name);
+    }
+
+    function fail(error) {
+        alert('Failed to get parent directory: ' + error.code);
+    }
+
+    // Get the parent DirectoryEntry
+    entry.getParent(success, fail);
+
+createReader
+------------
+
+Creates a new DirectoryReader to read entries in a directory.
+
+__Quick Example__
+
+    // create a directory reader
+    var directoryReader = entry.createReader();
+
+getDirectory
+------------
+
+Creates or looks up an existing directory.  An error results if the app attempts to:
+
+- create a directory whose immediate parent does not yet exist.
+
+__Parameters:__
+
+- __path__: The path to the directory to be looked up or created.  Either an absolute path, or a relative path from this `DirectoryEntry`. _(DOMString)_
+- __options__: Options to specify whether the directory is to be created if it doesn't exist.  _(Flags)_
+- __successCallback__: A callback that executes with a `DirectoryEntry` object. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when creating or looking up the directory. Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(parent) {
+        console.log("Parent Name: " + parent.name);
+    }
+
+    function fail(error) {
+        alert("Unable to create new directory: " + error.code);
+    }
+
+    // Retrieve an existing directory, or create it if it does not already exist
+    entry.getDirectory("newDir", {create: true, exclusive: false}, success, fail);
+
+getFile
+-------
+
+Creates or looks up a file.  An error results if the app attempts to:
+
+- create a file whose immediate parent does not yet exist.
+
+__Parameters:__
+
+- __path__: The path to the file to be looked up or created.  Either an absolute path, or a relative path from this `DirectoryEntry`. _(DOMString)_
+- __options__: Options to specify whether the file is created if it doesn't exist.  _(Flags)_
+- __successCallback__: A callback that is passed a `FileEntry` object. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when creating or looking up the file. Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(parent) {
+        console.log("Parent Name: " + parent.name);
+    }
+
+    function fail(error) {
+        alert("Failed to retrieve file: " + error.code);
+    }
+
+    // Retrieve an existing file, or create it if it does not exist
+    entry.getFile("newFile.txt", {create: true, exclusive: false}, success, fail);
+
+removeRecursively
+-----------------
+
+Deletes a directory and all of its contents.  In the event of an error (such as trying to delete
+a directory containing a file that cannot be removed), some of the contents of the directory may
+be deleted.   An error results if the app attempts to:
+
+- delete the root directory of a filesystem.
+
+__Parameters:__
+
+- __successCallback__: A callback that executes after the `DirectoryEntry` has been deleted.  Invoked with no parameters. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when attempting to delete the `DirectoryEntry`. Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(parent) {
+        console.log("Remove Recursively Succeeded");
+    }
+
+    function fail(error) {
+        alert("Failed to remove directory or it's contents: " + error.code);
+    }
+
+    // remove the directory and all it's contents
+    entry.removeRecursively(success, fail);
+
+BlackBerry Quirks
+-----------------
+
+May fail with a `ControlledAccessException` in the following cases:
+
+- An app attempts to access a directory created by a previous installation of the app.
+
+> Solution: ensure temporary directories are cleaned manually, or by the application prior to reinstallation.
+
+- If the device is connected by USB.
+
+> Solution: disconnect the USB cable from the device and run again.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/directoryreader/directoryreader.md
----------------------------------------------------------------------
diff --git a/docs/directoryreader/directoryreader.md b/docs/directoryreader/directoryreader.md
new file mode 100644
index 0000000..b37edb9
--- /dev/null
+++ b/docs/directoryreader/directoryreader.md
@@ -0,0 +1,69 @@
+---
+license: 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.
+---
+
+DirectoryReader
+===============
+
+An object that lists files and directories within a directory, as
+defined in the
+[W3C Directories and Systems](http://www.w3.org/TR/file-system-api/)
+specification.
+
+Methods
+-------
+
+- __readEntries__: Read the entries in a directory.
+
+Supported Platforms
+-------------------
+
+- Android
+- BlackBerry WebWorks (OS 5.0 and higher)
+- iOS
+- Windows Phone 7 and 8
+- Windows 8
+
+readEntries
+-----------
+
+Read the entries in this directory.
+
+__Parameters:__
+
+- __successCallback__: A callback that is passed an array of `FileEntry` and `DirectoryEntry` objects. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when retrieving the directory listing. Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(entries) {
+        var i;
+        for (i=0; i<entries.length; i++) {
+            console.log(entries[i].name);
+        }
+    }
+
+    function fail(error) {
+        alert("Failed to list directory contents: " + error.code);
+    }
+
+    // Get a directory reader
+    var directoryReader = dirEntry.createReader();
+
+    // Get a list of all the entries in the directory
+    directoryReader.readEntries(success,fail);

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/file.md
----------------------------------------------------------------------
diff --git a/docs/file.md b/docs/file.md
new file mode 100644
index 0000000..eed64c0
--- /dev/null
+++ b/docs/file.md
@@ -0,0 +1,91 @@
+---
+license: 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.
+---
+
+File
+==========
+
+> An API to read, write and navigate file system hierarchies, based on the [W3C File API](http://www.w3.org/TR/FileAPI).
+
+Objects
+-------
+
+- DirectoryEntry
+- DirectoryReader
+- File
+- FileEntry
+- FileError
+- FileReader
+- FileSystem
+- FileTransfer
+- FileTransferError
+- FileUploadOptions
+- FileUploadResult
+- FileWriter
+- Flags
+- LocalFileSystem
+- Metadata
+
+Permissions
+-----------
+
+### Android
+
+#### app/res/xml/config.xml
+
+    <plugin name="File" value="org.apache.cordova.FileUtils" />
+    <plugin name="FileTransfer" value="org.apache.cordova.FileTransfer" />
+
+#### app/AndroidManifest.xml
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+### Bada
+
+    No permissions are required.
+
+### BlackBerry WebWorks
+
+#### www/plugins.xml
+
+    <plugin name="File" value="org.apache.cordova.file.FileManager" />
+    <plugin name="FileTransfer" value="org.apache.cordova.http.FileTransfer" />
+
+#### www/config.xml
+
+    <feature id="blackberry.io.file" required="true" version="1.0.0.0" />
+    <feature id="blackberry.utils"   required="true" version="1.0.0.0" />
+    <feature id="blackberry.io.dir"  required="true" version="1.0.0.0" />
+    <rim:permissions>
+        <rim:permit>access_shared</rim:permit>
+    </rim:permissions>
+
+### iOS
+
+#### config.xml
+
+    <plugin name="File" value="CDVFile" />
+    <plugin name="FileTransfer" value="CDVFileTransfer" />
+
+### webOS
+
+    No permissions are required.
+
+### Windows Phone
+
+    No permissions are required.

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/fileentry/fileentry.md
----------------------------------------------------------------------
diff --git a/docs/fileentry/fileentry.md b/docs/fileentry/fileentry.md
new file mode 100644
index 0000000..f362833
--- /dev/null
+++ b/docs/fileentry/fileentry.md
@@ -0,0 +1,323 @@
+---
+license: 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.
+---
+
+FileEntry
+==========
+
+Represents a file on a file system, as defined in the
+[W3C Directories and Systems](http://www.w3.org/TR/file-system-api/)
+specification.
+
+Properties
+----------
+
+- __isFile__: Always true. _(boolean)_
+- __isDirectory__: Always false. _(boolean)_
+- __name__: The name of the `FileEntry`, excluding the path leading to it. _(DOMString)_
+- __fullPath__: The full absolute path from the root to the `FileEntry`. _(DOMString)_
+
+__NOTE:__ The following attribute is defined by the W3C specification,
+but is _not_ supported:
+
+- __filesystem__: The file system on which the `FileEntry` resides. _(FileSystem)_
+
+Methods
+-------
+
+- __getMetadata__: Look up metadata about a file.
+- __setMetadata__: Set metadata on a file.
+- __moveTo__: Move a file to a different location on the file system.
+- __copyTo__: Copy a file to a different location on the file system.
+- __toURL__: Return a URL that can be used to locate a file.
+- __remove__: Delete a file.
+- __getParent__: Look up the parent directory.
+- __createWriter__: Creates a `FileWriter` object that can be used to write to a file.
+- __file__: Creates a `File` object containing file properties.
+
+Supported Platforms
+-------------------
+
+- Android
+- BlackBerry WebWorks (OS 5.0 and higher)
+- iOS
+- Windows Phone 7 and 8
+- Windows 8
+
+getMetadata
+----------------
+
+Look up metadata about a file.
+
+__Parameters:__
+
+- __successCallback__: A callback that is passed a `Metadata` object. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when retrieving the `Metadata`. Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(metadata) {
+        console.log("Last Modified: " + metadata.modificationTime);
+    }
+
+    function fail(error) {
+        alert(error.code);
+    }
+
+    // Request the metadata object for this entry
+    entry.getMetadata(success, fail);
+
+setMetadata
+----------------
+
+Set metadata on a file.
+
+__Currently works only on iOS.__
+- this will set the extended attributes of a file.
+
+__Parameters:__
+
+- __successCallback__: A callback that executes when the metadata is set. _(Function)_
+- __errorCallback__: A callback that executes when the metadata is not successfully set. _(Function)_
+- __metadataObject__: An object that contains the metadata's keys and values. _(Object)_
+
+__Quick Example__
+
+    function success() {
+        console.log("The metadata was successfully set.");
+    }
+
+    function fail() {
+        alert("There was an error in setting the metadata");
+    }
+
+    // Set the metadata
+    entry.setMetadata(success, fail, { "com.apple.MobileBackup": 1});
+
+__iOS Quirk__
+
+- Only the `com.apple.MobileBackup` extended attribute is supported. Set the value to `1` to prevent the file from being backed up to iCloud. Set the value to `0` to re-enable the file to be backed up to iCloud.
+
+__Quick Example__
+
+    function setFileMetadata(localFileSystem, filePath, metadataKey, metadataValue)
+    {
+            var onSetMetadataWin = function() {
+              console.log("success setting metadata")
+            }
+        var onSetMetadataFail = function() {
+              console.log("error setting metadata")
+        }
+
+            var onGetFileWin = function(parent) {
+              var data = {};
+              data[metadataKey] = metadataValue;
+              parent.setMetadata(onSetMetadataWin, onSetMetadataFail, data);
+            }
+            var onGetFileFail = function() {
+              console.log("error getting file")
+            }
+
+            var onFSWin = function(fileSystem) {
+              fileSystem.root.getFile(filePath, {create: true, exclusive: false}, onGetFileWin, onGetFileFail);
+            }
+
+            var onFSFail = function(evt) {
+                  console.log(evt.target.error.code);
+            }
+
+            window.requestFileSystem(localFileSystem, 0, onFSWin, onFSFail);
+    }
+
+        setFileMetadata(LocalFileSystem.PERSISTENT, "Backups/sqlite.db", "com.apple.MobileBackup", 1);
+
+moveTo
+------
+
+Move a file to a different location on the file system. An error
+results if the app attempts to:
+
+- move a file into its parent if a name different from its current one isn't provided;
+- move a file to a path occupied by a directory;
+
+In addition, moving a file on top of an existing file attempts to
+delete and replace that file.
+
+__Parameters:__
+
+- __parent__: The parent directory to which to move the file. _(DirectoryEntry)_
+- __newName__: The new name of the file. Defaults to the current name if unspecified. _(DOMString)_
+- __successCallback__: A callback that is passed the new files `FileEntry` object. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when attempting to move the file.  Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(entry) {
+        console.log("New Path: " + entry.fullPath);
+    }
+
+    function fail(error) {
+        alert(error.code);
+    }
+
+    function moveFile(entry) {
+        var parent = document.getElementById('parent').value,
+            parentName = parent.substring(parent.lastIndexOf('/')+1),
+            parentEntry = new DirectoryEntry(parentName, parent);
+
+        // move the file to a new directory and rename it
+        entry.moveTo(parentEntry, "newFile.txt", success, fail);
+    }
+
+copyTo
+------
+
+Copy a file to a new location on the file system.  An error results if
+the app attempts to:
+
+- copy a file into its parent if a name different from its current one is not provided.
+
+__Parameters:__
+
+- __parent__: The parent directory to which to copy the file. _(DirectoryEntry)_
+- __newName__: The new name of the file. Defaults to the current name if unspecified. _(DOMString)_
+- __successCallback__: A callback that is passed the new file's `FileEntry` object. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when attempting to copy the file.  Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function win(entry) {
+        console.log("New Path: " + entry.fullPath);
+    }
+
+    function fail(error) {
+        alert(error.code);
+    }
+
+    function copyFile(entry) {
+        var parent = document.getElementById('parent').value,
+            parentName = parent.substring(parent.lastIndexOf('/')+1),
+            parentEntry = new DirectoryEntry(parentName, parent);
+
+        // copy the file to a new directory and rename it
+        entry.copyTo(parentEntry, "file.copy", success, fail);
+    }
+
+toURL
+-----
+
+Returns a URL that can be used to locate the file.
+
+__Quick Example__
+
+    // Request the URL for this entry
+    var fileURL = entry.toURL();
+    console.log(fileURL);
+
+remove
+------
+
+Deletes a file.
+
+__Parameters:__
+
+- __successCallback__: A callback that executes after the file has been deleted.  Invoked with no parameters. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when attempting to delete the file.  Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(entry) {
+        console.log("Removal succeeded");
+    }
+
+    function fail(error) {
+        alert('Error removing file: ' + error.code);
+    }
+
+    // remove the file
+    entry.remove(success, fail);
+
+getParent
+---------
+
+Look up the parent `DirectoryEntry` containing the file.
+
+__Parameters:__
+
+- __successCallback__: A callback that is passed the file's parent `DirectoryEntry`. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when attempting to retrieve the parent `DirectoryEntry`.  Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(parent) {
+        console.log("Parent Name: " + parent.name);
+    }
+
+    function fail(error) {
+        alert(error.code);
+    }
+
+    // Get the parent DirectoryEntry
+    entry.getParent(success, fail);
+
+createWriter
+------------
+
+Create a `FileWriter` object associated with the file represented by the `FileEntry`.
+
+__Parameters:__
+
+- __successCallback__: A callback that is passed a `FileWriter` object. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs while attempting to create the FileWriter.  Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(writer) {
+        writer.write("Some text to the file");
+    }
+
+    function fail(error) {
+        alert(error.code);
+    }
+
+    // create a FileWriter to write to the file
+    entry.createWriter(success, fail);
+
+file
+----
+
+Return a `File` object that represents the current state of the file
+that this `FileEntry` represents.
+
+__Parameters:__
+
+- __successCallback__: A callback that is passed a `File` object. _(Function)_
+- __errorCallback__: A callback that executes if an error occurs when creating the `File` object, such as when the file no longer exists.  Invoked with a `FileError` object. _(Function)_
+
+__Quick Example__
+
+    function success(file) {
+        console.log("File size: " + file.size);
+    }
+
+    function fail(error) {
+        alert("Unable to retrieve file properties: " + error.code);
+    }
+
+    // obtain properties of a file
+    entry.file(success, fail);

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/fileerror/fileerror.md
----------------------------------------------------------------------
diff --git a/docs/fileerror/fileerror.md b/docs/fileerror/fileerror.md
new file mode 100644
index 0000000..c34c6be
--- /dev/null
+++ b/docs/fileerror/fileerror.md
@@ -0,0 +1,51 @@
+---
+license: 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.
+---
+
+FileError
+========
+
+A `FileError` object is set when an error occurs in any of the File API methods.
+
+Properties
+----------
+
+- __code__: One of the predefined error codes listed below.
+
+Constants
+---------
+
+- `FileError.NOT_FOUND_ERR`
+- `FileError.SECURITY_ERR`
+- `FileError.ABORT_ERR`
+- `FileError.NOT_READABLE_ERR`
+- `FileError.ENCODING_ERR`
+- `FileError.NO_MODIFICATION_ALLOWED_ERR`
+- `FileError.INVALID_STATE_ERR`
+- `FileError.SYNTAX_ERR`
+- `FileError.INVALID_MODIFICATION_ERR`
+- `FileError.QUOTA_EXCEEDED_ERR`
+- `FileError.TYPE_MISMATCH_ERR`
+- `FileError.PATH_EXISTS_ERR`
+
+Description
+-----------
+
+The `FileError` object is the only parameter provided to any of the
+File API's error callbacks.  To determine the type of error, compare
+its `code` property to any of the listings above.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/fileobj/fileobj.md
----------------------------------------------------------------------
diff --git a/docs/fileobj/fileobj.md b/docs/fileobj/fileobj.md
new file mode 100644
index 0000000..ee5f1d4
--- /dev/null
+++ b/docs/fileobj/fileobj.md
@@ -0,0 +1,83 @@
+---
+license: 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.
+---
+
+File
+====
+
+This object contains attributes of a single file.
+
+Properties
+----------
+
+- __name__: The name of the file. _(DOMString)_
+- __fullPath__: The full path of the file including the file name. _(DOMString)_
+- __type__: The mime type of the file. _(DOMString)_
+- __lastModifiedDate__: The last time the file was modified. _(Date)_
+- __size__: The size of the file in bytes. _(long)_
+
+Methods
+-------
+
+- __slice__: Select only a portion of the file to be read.
+
+Details
+-------
+
+The `File` object contains attributes of a single file.  You can get
+an instance of a `File` object by calling a `FileEntry` object's
+`file()` method.
+
+Supported Platforms
+-------------------
+
+- Android
+- BlackBerry WebWorks (OS 5.0 and higher)
+- iOS
+- Windows Phone 7 and 8
+- Windows 8
+
+slice
+--------------
+
+Return a new `File` object, for which `FileReader` returns only the
+specified portion of the file.  Negative values for `start` or `end`
+are measured from the end of the file.  Indexes are positioned
+relative to the current slice. (See the full example below.)
+
+__Parameters:__
+
+- __start__: The index of the first byte to read, inclusive.
+- __end__: The index of the byte after the last one to read.
+
+__Quick Example__
+
+    var slicedFile = file.slice(10, 30);
+
+__Full Example__
+
+    var slice1 = file.slice(100, 400);
+    var slice2 = slice1.slice(20, 35);
+
+    var slice3 = file.slice(120, 135);
+    // slice2 and slice3 are equivalent.
+
+__Supported Platforms:__
+
+- Android
+- iOS

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/filereader/filereader.md
----------------------------------------------------------------------
diff --git a/docs/filereader/filereader.md b/docs/filereader/filereader.md
new file mode 100644
index 0000000..25f6ff0
--- /dev/null
+++ b/docs/filereader/filereader.md
@@ -0,0 +1,259 @@
+---
+license: 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.
+---
+
+FileReader
+==========
+
+The `FileReader` allows basic access to a file.
+
+Properties
+----------
+
+- __readyState__: One of the reader's three possible states, either `EMPTY`, `LOADING` or `DONE`.
+- __result__: The contents of the file that have been read. _(DOMString)_
+- __error__: An object containing errors. _(FileError)_
+- __onloadstart__: Called when the read starts. _(Function)_
+- __onload__: Called when the read has successfully completed. _(Function)_
+- __onabort__: Called when the read has been aborted. For instance, by invoking the `abort()` method. _(Function)_
+- __onerror__: Called when the read has failed. _(Function)_
+- __onloadend__: Called when the request has completed (either in success or failure).  _(Function)_
+
+__NOTE:__ The following porperty is not supported:
+
+- __onprogress__: Called while reading the file, reporting progress in terms of `progress.loaded`/`progress.total`. _(Function)_
+
+Methods
+-------
+
+- __abort__: Aborts reading file.
+- __readAsDataURL__: Read file and return data as a base64-encoded data URL.
+- __readAsText__: Reads text file.
+- __readAsBinaryString__: Reads file as binary and returns a binary string.
+- __readAsArrayBuffer__: Reads file as an `ArrayBuffer`.
+
+Details
+-------
+
+The `FileReader` object offers a way to read files from the device's
+file system.  Files can be read as text or as a base64 data-encoded
+string.  Event listeners receive the `loadstart`, `progress`, `load`,
+`loadend`, `error`, and `abort` events.
+
+Supported Platforms
+-------------------
+
+- Android
+- BlackBerry WebWorks (OS 5.0 and higher)
+- iOS
+- Windows Phone 7 and 8
+- Windows 8
+
+Read As Data URL
+----------------
+
+__Parameters:__
+
+- __file__: the file object to read.
+
+Quick Example
+-------------
+
+    function win(file) {
+        var reader = new FileReader();
+        reader.onloadend = function (evt) {
+            console.log("read success");
+            console.log(evt.target.result);
+        };
+        reader.readAsDataURL(file);
+    };
+
+    var fail = function (evt) {
+        console.log(error.code);
+    };
+
+    entry.file(win, fail);
+
+Read As Text
+------------
+
+__Parameters:__
+
+- __file__: the file object to read.
+- __encoding__: the encoding to use to encode the file's content. Default is UTF8.
+
+Quick Example
+-------------
+
+    function win(file) {
+        var reader = new FileReader();
+        reader.onloadend = function (evt) {
+            console.log("read success");
+            console.log(evt.target.result);
+        };
+        reader.readAsText(file);
+    };
+
+    var fail = function (evt) {
+        console.log(error.code);
+    };
+
+    entry.file(win, fail);
+
+Abort Quick Example
+-------------------
+
+    function win(file) {
+        var reader = new FileReader();
+        reader.onloadend = function(evt) {
+            console.log("read success");
+            console.log(evt.target.result);
+        };
+        reader.readAsText(file);
+        reader.abort();
+    };
+
+    function fail(error) {
+        console.log(error.code);
+    }
+
+    entry.file(win, fail);
+
+Full Example
+------------
+
+    <!DOCTYPE html>
+    <html>
+      <head>
+        <title>FileReader Example</title>
+
+        <script type="text/javascript" charset="utf-8" src="cordova-x.x.x.js"></script>
+        <script type="text/javascript" charset="utf-8">
+
+        // Wait for device API libraries to load
+        //
+        function onLoad() {
+            document.addEventListener("deviceready", onDeviceReady, false);
+        }
+
+        // device APIs are available
+        //
+        function onDeviceReady() {
+            window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail);
+        }
+
+        function gotFS(fileSystem) {
+            fileSystem.root.getFile("readme.txt", null, gotFileEntry, fail);
+        }
+
+        function gotFileEntry(fileEntry) {
+            fileEntry.file(gotFile, fail);
+        }
+
+        function gotFile(file){
+            readDataUrl(file);
+            readAsText(file);
+        }
+
+        function readDataUrl(file) {
+            var reader = new FileReader();
+            reader.onloadend = function(evt) {
+                console.log("Read as data URL");
+                console.log(evt.target.result);
+            };
+            reader.readAsDataURL(file);
+        }
+
+        function readAsText(file) {
+            var reader = new FileReader();
+            reader.onloadend = function(evt) {
+                console.log("Read as text");
+                console.log(evt.target.result);
+            };
+            reader.readAsText(file);
+        }
+
+        function fail(evt) {
+            console.log(evt.target.error.code);
+        }
+
+        </script>
+      </head>
+      <body>
+        <h1>Example</h1>
+        <p>Read File</p>
+      </body>
+    </html>
+
+iOS Quirks
+----------
+- The __encoding__ parameter is not supported, and UTF8 encoding is always in effect.
+
+Read As Binary String
+---------------------
+
+Currently supported on iOS and Android only.
+
+__Parameters:__
+
+- __file__: the file object to read.
+
+Quick Example
+-------------
+
+    function win(file) {
+        var reader = new FileReader();
+        reader.onloadend = function (evt) {
+            console.log("read success");
+            console.log(evt.target.result);
+        };
+        reader.readAsBinaryString(file);
+    };
+
+    var fail = function (evt) {
+        console.log(error.code);
+    };
+
+    entry.file(win, fail);
+
+Read As Array Buffer
+--------------------
+
+Currently supported on iOS and Android only.
+
+__Parameters:__
+
+- __file__:  the file object to read.
+
+Quick Example
+-------------
+
+    function win(file) {
+        var reader = new FileReader();
+        reader.onloadend = function (evt) {
+            console.log("read success");
+            console.log(new Uint8Array(evt.target.result));
+        };
+        reader.readAsArrayBuffer(file);
+    };
+
+    var fail = function (evt) {
+        console.log(error.code);
+    };
+
+    entry.file(win, fail);

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/filesystem/filesystem.md
----------------------------------------------------------------------
diff --git a/docs/filesystem/filesystem.md b/docs/filesystem/filesystem.md
new file mode 100644
index 0000000..c8eaa2c
--- /dev/null
+++ b/docs/filesystem/filesystem.md
@@ -0,0 +1,95 @@
+---
+license: 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.
+---
+
+FileSystem
+==========
+
+This object represents a file system.
+
+Properties
+----------
+
+- __name__: The name of the file system. _(DOMString)_
+- __root__: The root directory of the file system. _(DirectoryEntry)_
+
+Details
+-------
+
+The `FileSystem` object represents information about the file system.
+The name of the file system is unique across the list of exposed
+file systems.  The root property contains a `DirectoryEntry` object
+that represents the file system's root directory.
+
+Supported Platforms
+-------------------
+
+- Android
+- BlackBerry WebWorks (OS 5.0 and higher)
+- iOS
+- Windows Phone 7 and 8
+- Windows 8
+
+File System Quick Example
+-------------------------
+
+    function onSuccess(fileSystem) {
+        console.log(fileSystem.name);
+        console.log(fileSystem.root.name);
+    }
+
+    // request the persistent file system
+    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onSuccess, null);
+
+Full Example
+------------
+
+    <!DOCTYPE html>
+    <html>
+      <head>
+        <title>File System Example</title>
+
+        <script type="text/javascript" charset="utf-8" src="cordova-x.x.x.js"></script>
+        <script type="text/javascript" charset="utf-8">
+
+        // Wait for device API libraries to load
+        //
+        document.addEventListener("deviceready", onDeviceReady, false);
+
+        // device APIs are available
+        //
+        function onDeviceReady() {
+            window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFileSystemSuccess, fail);
+        }
+
+        function onFileSystemSuccess(fileSystem) {
+            console.log(fileSystem.name);
+            console.log(fileSystem.root.name);
+        }
+
+        function fail(evt) {
+            console.log(evt.target.error.code);
+        }
+
+        </script>
+      </head>
+      <body>
+        <h1>Example</h1>
+        <p>File System</p>
+      </body>
+    </html>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/fileuploadoptions/fileuploadoptions.md
----------------------------------------------------------------------
diff --git a/docs/fileuploadoptions/fileuploadoptions.md b/docs/fileuploadoptions/fileuploadoptions.md
new file mode 100644
index 0000000..e0e6fe2
--- /dev/null
+++ b/docs/fileuploadoptions/fileuploadoptions.md
@@ -0,0 +1,47 @@
+---
+license: 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.
+---
+
+FileUploadOptions
+========
+
+A `FileUploadOptions` object can be passed to the `FileTransfer`
+object's `upload()` method to specify additional parameters to the
+upload script.
+
+Properties
+----------
+
+- __fileKey__: The name of the form element.  Defaults to `file`. (DOMString)
+- __fileName__: The file name to use when saving the file on the server.  Defaults to `image.jpg`. (DOMString)
+- __mimeType__: The mime type of the data to upload.  Defaults to `image/jpeg`. (DOMString)
+- __params__: A set of optional key/value pairs to pass in the HTTP request. (Object)
+- __chunkedMode__: Whether to upload the data in chunked streaming mode. Defaults to `true`. (Boolean)
+- __headers__: A map of header name/header values. Use an array to specify more than one value. (Object)
+
+Description
+-----------
+
+A `FileUploadOptions` object can be passed to the `FileTransfer`
+object's `upload()` method to specify additional parameters to the
+upload script.
+
+WP7 Quirk
+---------
+
+- __chunkedMode:__: Ignored on WP7.

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/fileuploadresult/fileuploadresult.md
----------------------------------------------------------------------
diff --git a/docs/fileuploadresult/fileuploadresult.md b/docs/fileuploadresult/fileuploadresult.md
new file mode 100644
index 0000000..4d9305a
--- /dev/null
+++ b/docs/fileuploadresult/fileuploadresult.md
@@ -0,0 +1,42 @@
+---
+license: 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.
+---
+
+FileUploadResult
+========
+
+A `FileUploadResult` object is passed to the success callback of the
+`FileTransfer` object's `upload()` method.
+
+Properties
+----------
+
+- __bytesSent__: The number of bytes sent to the server as part of the upload. (long)
+- __responseCode__: The HTTP response code returned by the server. (long)
+- __response__: The HTTP response returned by the server. (DOMString)
+
+Description
+-----------
+
+The `FileUploadResult` object is returned via the success callback of
+the `FileTransfer` object's `upload()` method.
+
+iOS Quirks
+----------
+
+- Does not support `responseCode` or `bytesSent`.

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/filewriter/filewriter.md
----------------------------------------------------------------------
diff --git a/docs/filewriter/filewriter.md b/docs/filewriter/filewriter.md
new file mode 100644
index 0000000..2269c06
--- /dev/null
+++ b/docs/filewriter/filewriter.md
@@ -0,0 +1,233 @@
+---
+license: 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.
+---
+
+FileWriter
+==========
+
+As object that allows you to create and write data to a file.
+
+Properties
+----------
+
+- __readyState__: One of the three possible states, either `INIT`, `WRITING`, or `DONE`.
+- __fileName__: The name of the file to be written. _(DOMString)_
+- __length__: The length of the file to be written. _(long)_
+- __position__: The current position of the file pointer. _(long)_
+- __error__: An object containing errors. _(FileError)_
+- __onwritestart__: Called when the write starts. _(Function)_
+- __onwrite__: Called when the request has completed successfully.  _(Function)_
+- __onabort__: Called when the write has been aborted. For instance, by invoking the abort() method. _(Function)_
+- __onerror__: Called when the write has failed. _(Function)_
+- __onwriteend__: Called when the request has completed (either in success or failure).  _(Function)_
+
+The following property is _not_ supported:
+
+- __onprogress__: Called while writing the file, reporting progress in terms of `progress.loaded`/`progress.total`. _(Function)_
+Methods
+-------
+
+- __abort__: Aborts writing the file.
+- __seek__: Moves the file pointer to the specified byte.
+- __truncate__: Shortens the file to the specified length.
+- __write__: Writes data to the file.
+
+Details
+-------
+
+The `FileWriter` object offers a way to write UTF-8 encoded files to
+the device file system.  Applications respond to `writestart`,
+`progress`, `write`, `writeend`, `error`, and `abort` events.
+
+Each `FileWriter` corresponds to a single file, to which data can be
+written many times.  The `FileWriter` maintains the file's `position`
+and `length` attributes, which allow the app to `seek` and `write`
+anywhere in the file. By default, the `FileWriter` writes to the
+beginning of the file, overwriting existing data. Set the optional
+`append` boolean to `true` in the `FileWriter`'s constructor to
+write to the end of the file.
+
+Text data is supported by all platforms listed below. Text is encoded as UTF-8 before being written to the filesystem. Some platforms also support binary data, which can be passed in as either an ArrayBuffer or a Blob.
+
+Supported Platforms
+-------------------
+
+### Text and Binary suport
+
+- Android
+- iOS
+
+### Text only support
+
+- BlackBerry WebWorks (OS 5.0 and higher)
+- Windows Phone 7 and 8
+- Windows 8
+
+Seek Quick Example
+------------------------------
+
+    function win(writer) {
+        // fast forwards file pointer to end of file
+        writer.seek(writer.length);
+    };
+
+    var fail = function(evt) {
+        console.log(error.code);
+    };
+
+    entry.createWriter(win, fail);
+
+Truncate Quick Example
+--------------------------
+
+    function win(writer) {
+        writer.truncate(10);
+    };
+
+    var fail = function(evt) {
+        console.log(error.code);
+    };
+
+    entry.createWriter(win, fail);
+
+Write Quick Example
+-------------------
+
+    function win(writer) {
+        writer.onwrite = function(evt) {
+            console.log("write success");
+        };
+        writer.write("some sample text");
+    };
+
+    var fail = function(evt) {
+        console.log(error.code);
+    };
+
+    entry.createWriter(win, fail);
+
+Binary Write Quick Example
+--------------------------
+
+    function win(writer) {
+        var data = new ArrayBuffer(5),
+            dataView = new Int8Array(data);
+        for (i=0; i < 5; i++) {
+            dataView[i] = i;
+        }
+        writer.onwrite = function(evt) {
+            console.log("write success");
+        };
+        writer.write(data);
+    };
+
+    var fail = function(evt) {
+        console.log(error.code);
+    };
+
+    entry.createWriter(win, fail);
+
+Append Quick Example
+--------------------
+
+    function win(writer) {
+        writer.onwrite = function(evt) {
+        console.log("write success");
+    };
+    writer.seek(writer.length);
+        writer.write("appended text");
+    };
+
+    var fail = function(evt) {
+        console.log(error.code);
+    };
+
+    entry.createWriter(win, fail);
+
+Abort Quick Example
+-------------------
+
+    function win(writer) {
+        writer.onwrite = function(evt) {
+            console.log("write success");
+        };
+        writer.write("some sample text");
+        writer.abort();
+    };
+
+    var fail = function(evt) {
+        console.log(error.code);
+    };
+
+    entry.createWriter(win, fail);
+
+Full Example
+------------
+    <!DOCTYPE html>
+    <html>
+      <head>
+        <title>FileWriter Example</title>
+
+        <script type="text/javascript" charset="utf-8" src="cordova-x.x.x.js"></script>
+        <script type="text/javascript" charset="utf-8">
+
+        // Wait for device API libraries to load
+        //
+        document.addEventListener("deviceready", onDeviceReady, false);
+
+        // device APIs are available
+        //
+        function onDeviceReady() {
+            window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail);
+        }
+
+        function gotFS(fileSystem) {
+            fileSystem.root.getFile("readme.txt", {create: true, exclusive: false}, gotFileEntry, fail);
+        }
+
+        function gotFileEntry(fileEntry) {
+            fileEntry.createWriter(gotFileWriter, fail);
+        }
+
+        function gotFileWriter(writer) {
+            writer.onwriteend = function(evt) {
+                console.log("contents of file now 'some sample text'");
+                writer.truncate(11);
+                writer.onwriteend = function(evt) {
+                    console.log("contents of file now 'some sample'");
+                    writer.seek(4);
+                    writer.write(" different text");
+                    writer.onwriteend = function(evt){
+                        console.log("contents of file now 'some different text'");
+                    }
+                };
+            };
+            writer.write("some sample text");
+        }
+
+        function fail(error) {
+            console.log(error.code);
+        }
+
+        </script>
+      </head>
+      <body>
+        <h1>Example</h1>
+        <p>Write File</p>
+      </body>
+    </html>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/flags/flags.md
----------------------------------------------------------------------
diff --git a/docs/flags/flags.md b/docs/flags/flags.md
new file mode 100644
index 0000000..504f323
--- /dev/null
+++ b/docs/flags/flags.md
@@ -0,0 +1,49 @@
+---
+license: 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.
+---
+
+Flags
+=====
+
+Supplies arguments to the `DirectoryEntry` object's `getFile()` and
+`getDirectory()` methods, which look up or create files and
+directories, respectively.
+
+Properties
+----------
+
+- __create__: Indicates that the file or directory should be created if it does not already exist. _(boolean)_
+- __exclusive__: Has has no effect by itself, but when used with `create` causes the file or directory creation to fail if the target path already exists. _(boolean)_
+
+Supported Platforms
+-------------------
+
+- Android
+- BlackBerry WebWorks (OS 5.0 and higher)
+- iOS
+- Windows Phone 7 and 8
+- Windows 8
+
+Quick Example
+-------------
+
+    // Get the data directory, creating it if it doesn't exist.
+    dataDir = fileSystem.root.getDirectory("data", {create: true});
+
+    // Create the lock file, if and only if it doesn't exist.
+    lockFile = dataDir.getFile("lockfile.txt", {create: true, exclusive: true});

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/localfilesystem/localfilesystem.md
----------------------------------------------------------------------
diff --git a/docs/localfilesystem/localfilesystem.md b/docs/localfilesystem/localfilesystem.md
new file mode 100644
index 0000000..2b933c9
--- /dev/null
+++ b/docs/localfilesystem/localfilesystem.md
@@ -0,0 +1,110 @@
+---
+license: 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.
+---
+
+LocalFileSystem
+===============
+
+This object provides a way to obtain root file systems.
+
+Methods
+----------
+
+- __requestFileSystem__: Requests a filesystem. _(Function)_
+- __resolveLocalFileSystemURI__: Retrieve a `DirectoryEntry` or `FileEntry` using local URI. _(Function)_
+
+Constants
+---------
+
+- `LocalFileSystem.PERSISTENT`: Used for storage that should not be removed by the user agent without application or user permission.
+- `LocalFileSystem.TEMPORARY`: Used for storage with no guarantee of persistence.
+
+Details
+-------
+
+The `LocalFileSystem` object methods are defined on the `window` object.
+
+Supported Platforms
+-------------------
+
+- Android
+- BlackBerry WebWorks (OS 5.0 and higher)
+- iOS
+- Windows Phone 7 and 8
+- Windows 8
+
+Request File System Quick Example
+---------------------------------
+
+    function onSuccess(fileSystem) {
+        console.log(fileSystem.name);
+    }
+
+    // request the persistent file system
+    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onSuccess, onError);
+
+Resolve Local File System URI Quick Example
+-------------------------------------------
+
+    function onSuccess(fileEntry) {
+        console.log(fileEntry.name);
+    }
+
+    window.resolveLocalFileSystemURI("file:///example.txt", onSuccess, onError);
+
+Full Example
+------------
+
+    <!DOCTYPE html>
+    <html>
+      <head>
+        <title>Local File System Example</title>
+
+        <script type="text/javascript" charset="utf-8" src="cordova-x.x.x.js"></script>
+        <script type="text/javascript" charset="utf-8">
+
+        // Wait for device API libraries to load
+        //
+        document.addEventListener("deviceready", onDeviceReady, false);
+
+        // device APIs are available
+        //
+        function onDeviceReady() {
+            window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFileSystemSuccess, fail);
+            window.resolveLocalFileSystemURI("file:///example.txt", onResolveSuccess, fail);
+        }
+
+        function onFileSystemSuccess(fileSystem) {
+            console.log(fileSystem.name);
+        }
+
+        function onResolveSuccess(fileEntry) {
+            console.log(fileEntry.name);
+        }
+
+        function fail(evt) {
+            console.log(evt.target.error.code);
+        }
+
+        </script>
+      </head>
+      <body>
+        <h1>Example</h1>
+        <p>Local File System</p>
+      </body>
+    </html>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/docs/metadata/metadata.md
----------------------------------------------------------------------
diff --git a/docs/metadata/metadata.md b/docs/metadata/metadata.md
new file mode 100644
index 0000000..bfa3a46
--- /dev/null
+++ b/docs/metadata/metadata.md
@@ -0,0 +1,54 @@
+---
+license: 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.
+---
+
+Metadata
+==========
+
+An interface that supplies information about the state of a file or directory.
+
+Properties
+----------
+
+- __modificationTime__: The time when the file or directory was last modified. _(Date)_
+
+Details
+-------
+
+The `Metadata` object represents information about the state of a file
+or directory.  Calling a `DirectoryEntry` or `FileEntry` object's
+`getMetadata()` method results in a `Metadata` instance.
+
+Supported Platforms
+-------------------
+
+- Android
+- BlackBerry WebWorks (OS 5.0 and higher)
+- iOS
+- Windows Phone 7 and 8
+- Windows 8
+
+Quick Example
+-------------
+
+    function win(metadata) {
+        console.log("Last Modified: " + metadata.modificationTime);
+    }
+
+    // Request the metadata object for this entry
+    entry.getMetadata(win, null);

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index 069c7ba..b107478 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -2,8 +2,7 @@
 
 <plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
 xmlns:android="http://schemas.android.com/apk/res/android"
-id="org.apache.cordova.core.FileUtils"
-    version="0.1.0">
+id="org.apache.cordova.core.FileUtils" version="0.1.0">
     <name>File</name>
 
     <js-module src="www/DirectoryEntry.js" name="DirectoryEntry">

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/html/HtmlReporter.js
----------------------------------------------------------------------
diff --git a/test/autotest/html/HtmlReporter.js b/test/autotest/html/HtmlReporter.js
new file mode 100644
index 0000000..7d9d924
--- /dev/null
+++ b/test/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);

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/html/HtmlReporterHelpers.js
----------------------------------------------------------------------
diff --git a/test/autotest/html/HtmlReporterHelpers.js b/test/autotest/html/HtmlReporterHelpers.js
new file mode 100644
index 0000000..745e1e0
--- /dev/null
+++ b/test/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];
+  }
+};
+

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/html/ReporterView.js
----------------------------------------------------------------------
diff --git a/test/autotest/html/ReporterView.js b/test/autotest/html/ReporterView.js
new file mode 100644
index 0000000..6a6d005
--- /dev/null
+++ b/test/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);
+
+

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/html/SpecView.js
----------------------------------------------------------------------
diff --git a/test/autotest/html/SpecView.js b/test/autotest/html/SpecView.js
new file mode 100644
index 0000000..e8a3c23
--- /dev/null
+++ b/test/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);

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/html/SuiteView.js
----------------------------------------------------------------------
diff --git a/test/autotest/html/SuiteView.js b/test/autotest/html/SuiteView.js
new file mode 100644
index 0000000..19a1efa
--- /dev/null
+++ b/test/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);
+


[2/5] added docs + tests

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/tests/file.tests.js
----------------------------------------------------------------------
diff --git a/test/autotest/tests/file.tests.js b/test/autotest/tests/file.tests.js
new file mode 100644
index 0000000..c481ed3
--- /dev/null
+++ b/test/autotest/tests/file.tests.js
@@ -0,0 +1,3574 @@
+/*
+ *
+ * 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.
+ *
+*/
+
+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("file.spec.1 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("file.spec.2 should define LocalFileSystem constants", function() {
+            expect(LocalFileSystem.TEMPORARY).toBe(0);
+            expect(LocalFileSystem.PERSISTENT).toBe(1);
+        });
+
+        describe('window.requestFileSystem', function() {
+            it("file.spec.3 should be defined", function() {
+                expect(window.requestFileSystem).toBeDefined();
+            });
+            it("file.spec.4 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("file.spec.5 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("file.spec.6 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("file.spec.7 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("file.spec.3 should be defined", function() {
+                expect(window.resolveLocalFileSystemURI).toBeDefined();
+            });
+            it("file.spec.9 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("file.spec.10 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("file.spec.11 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("file.spec.12 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("file.spec.13 should exist and have the right properties", function() {
+            var metadata = new Metadata();
+            expect(metadata).toBeDefined();
+            expect(metadata.modificationTime).toBeDefined();
+        });
+    });
+
+    describe('Flags interface', function() {
+        it("file.spec.13 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("file.spec.15 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("file.spec.16 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("file.spec.17 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("file.spec.18 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("file.spec.19 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("file.spec.20 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("file.spec.21 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("file.spec.22 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("file.spec.23 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("file.spec.24 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("file.spec.25 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("file.spec.26 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("file.spec.27 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("file.spec.28 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("file.spec.29 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("file.spec.30 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("file.spec.31 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("file.spec.32 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("file.spec.33 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("file.spec.34 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("file.spec.35 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("file.spec.36 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("file.spec.37 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("file.spec.38 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("file.spec.39 constructor should be defined", function() {
+            expect(File).toBeDefined();
+            expect(typeof File).toBe('function');
+        });
+        it("file.spec.40 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("file.spec.41 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("file.spec.42 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.spec.43 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.spec.44 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("file.spec.45 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("file.spec.46 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("file.spec.47 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("file.spec.48 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("file.spec.49 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("file.spec.50 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("file.spec.51 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("file.spec.52 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("file.spec.53 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("file.spec.54 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("file.spec.55 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("file.spec.56 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("file.spec.57 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("file.spec.58 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("file.spec.59 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("file.spec.60 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("file.spec.61 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("file.spec.62 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("file.spec.63 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(

<TRUNCATED>

[4/5] added docs + tests

Posted by st...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/html/TrivialReporter.js
----------------------------------------------------------------------
diff --git a/test/autotest/html/TrivialReporter.js b/test/autotest/html/TrivialReporter.js
new file mode 100644
index 0000000..167ac50
--- /dev/null
+++ b/test/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;
+};

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/index.html
----------------------------------------------------------------------
diff --git a/test/autotest/index.html b/test/autotest/index.html
new file mode 100644
index 0000000..6ebccbd
--- /dev/null
+++ b/test/autotest/index.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!--
+
+ 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.
+
+-->
+
+
+<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-incl.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/datauri.html" class="btn large" style="width:100%;">Run Data URI 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/globalization.html" class="btn large" style="width:100%;">Run Globalization 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>
+    <a href="pages/bridge.html" class="btn large" style="width:100%;">Run Bridge Tests</a>
+
+    <h2> </h2><div class="backBtn" onclick="backHome();">Back</div>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/jasmine.css
----------------------------------------------------------------------
diff --git a/test/autotest/jasmine.css b/test/autotest/jasmine.css
new file mode 100644
index 0000000..826e575
--- /dev/null
+++ b/test/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; }

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/jasmine.js
----------------------------------------------------------------------
diff --git a/test/autotest/jasmine.js b/test/autotest/jasmine.js
new file mode 100644
index 0000000..bccb66c
--- /dev/null
+++ b/test/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
+};

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/acf384a6/test/autotest/pages/.DS_Store
----------------------------------------------------------------------
diff --git a/test/autotest/pages/.DS_Store b/test/autotest/pages/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/test/autotest/pages/.DS_Store differ