You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by an...@apache.org on 2012/07/26 23:19:04 UTC

[1/2] git commit: first commit pluginstall with git repositories support

Updated Branches:
  refs/heads/pluginstall-with-git-support [created] 4f8ab9d0a


first commit pluginstall with git repositories support


Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/commit/4f8ab9d0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/tree/4f8ab9d0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/diff/4f8ab9d0

Branch: refs/heads/pluginstall-with-git-support
Commit: 4f8ab9d0aadb02042a057aa2ed7b7e47a691c2fc
Parents: 68340ce
Author: Anis Kadri <an...@gmail.com>
Authored: Thu Jul 26 14:18:50 2012 -0700
Committer: Anis Kadri <an...@gmail.com>
Committed: Thu Jul 26 14:18:50 2012 -0700

----------------------------------------------------------------------
 LICENSE                                            |   14 +
 README.md                                          |   32 +-
 cli.js                                             |   55 +
 package.json                                       |   30 +
 platforms.json                                     |    1 +
 platforms/android.js                               |  121 ++
 platforms/ios.js                                   |  131 +++
 pluginstall.js                                     |   52 +
 test/android-install.js                            |  115 ++
 test/initialize.js                                 |   26 +
 test/ios-install.js                                |  144 +++
 test/parseXml.js                                   |   31 +
 test/plugin/plugin.xml                             |   48 +
 .../src/ios/ChildBrowser.bundle/arrow_left.png     |  Bin 0 -> 2946 bytes
 .../src/ios/ChildBrowser.bundle/arrow_left@2x.png  |  Bin 0 -> 2946 bytes
 .../src/ios/ChildBrowser.bundle/arrow_right.png    |  Bin 0 -> 2946 bytes
 .../src/ios/ChildBrowser.bundle/arrow_right@2x.png |  Bin 0 -> 2946 bytes
 .../src/ios/ChildBrowser.bundle/but_refresh.png    |  Bin 0 -> 3369 bytes
 .../src/ios/ChildBrowser.bundle/but_refresh@2x.png |  Bin 0 -> 3369 bytes
 .../plugin/src/ios/ChildBrowser.bundle/compass.png |  Bin 0 -> 3035 bytes
 .../src/ios/ChildBrowser.bundle/compass@2x.png     |  Bin 0 -> 3035 bytes
 test/plugin/src/ios/ChildBrowserCommand.h          |   30 +
 test/plugin/src/ios/ChildBrowserCommand.m          |   86 ++
 test/plugin/src/ios/ChildBrowserViewController.h   |   54 +
 test/plugin/src/ios/ChildBrowserViewController.m   |  239 ++++
 test/plugin/src/ios/ChildBrowserViewController.xib |  875 +++++++++++++++
 test/plugin/www/childbrowser/image.jpg             |    1 +
 test/project/AndroidManifest.orig.xml              |   50 +
 test/project/AndroidManifest.xml                   |    2 +
 .../SampleApp.xcodeproj/project.orig.pbxproj       |  498 ++++++++
 test/project/SampleApp/PhoneGap.orig.plist         |   53 +
 test/project/assets/www/childbrowser/image.jpg     |    1 +
 test/project/res/xml/plugins.orig.xml              |   19 +
 test/project/res/xml/plugins.xml                   |    2 +
 util/asyncCopy.js                                  |    7 +
 util/ncallbacks.js                                 |   12 +
 36 files changed, 2715 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..45f03a3
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,14 @@
+   Copyright 2012 Andrew Lunny, Adobe Systems
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 2d78d3a..d70554d 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,26 @@
-# Cordova Laboratory
+# Pluginstall
 
-> Caution: Safety Goggles are Recommended!
+> script to install Cordova plugins
 
-## Purpose
+## Usage
 
-The purpose of this repo is for experimental code. Examples include demo apps,
-native api explorations, or anything really that does not fit in an existing Cordova platform.
+    pluginstall PLATFORM PROJECT-PATH PLUGIN-PATH
 
-## Project Organization
+Example:
 
-> Everyone works on a branch
+    pluginstall android . ~/plugins/ChildBrowser
 
-`master` branch should *never* have content.
+## Supported Platforms
 
-Each project should create a separate branch to work on. There are major benefits
-to this practice:
+* Android
+* iOS
 
-- Each project has an isolate git history, which allows for easy migration to
-  a new git repository;
-- Working directory is not polluted with the files of other projects.
-- Projects will not step on each others toes.
+## plugin.xml Format
+
+see https://github.com/alunny/cordova-plugin-spec
+
+Currently support the June 8 revision.
+
+## License
+
+Apache

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/cli.js
----------------------------------------------------------------------
diff --git a/cli.js b/cli.js
new file mode 100755
index 0000000..e5a62b8
--- /dev/null
+++ b/cli.js
@@ -0,0 +1,55 @@
+#!/usr/bin/env node
+var pluginstall = require('./pluginstall'),
+    spawn = require('child_process').spawn,
+    platform, projectDir, pluginDir,
+    config, plugin, package,
+    gitProc;
+if (process.argv[0] == 'node') {
+    process.argv.shift();
+}
+
+function installPlugin(platform, projectDir, pluginDir) {
+    config = pluginstall.init(platform, projectDir, pluginDir);
+    plugin = pluginstall.parseXml(config);
+
+    pluginstall.installPlugin(config, plugin, function (err) {
+        if (err) {
+            console.error(err);
+        } else {
+            console.log('plugin installed');
+        }
+    });
+}
+
+process.argv.shift(); // skip "cli.js"
+
+if (process.argv.length == 0) {
+    console.log('Usage: pluginstall [platform] [project directory] [plugin directory]');
+} else if (process.argv[0] === '-v') {
+    package = require('./package')
+    console.log('pluginstall version ' + package.version);
+} else {
+    platform = process.argv.shift();
+    projectDir = process.argv.shift();
+    pluginDir = process.argv.shift();
+
+    // clone from git repository
+    if(pluginDir.indexOf('https://') == 0 || pluginDir.indexOf('git://') == 0) {
+        // FIXME: need to find a portable way to get the os temporary directory 
+        tmpPluginDir = '/tmp/plugin';
+        gitProc = spawn('git', ['clone', pluginDir, tmpPluginDir], {cwd: '/tmp'});
+        gitProc.on('exit', function(code) {
+            if(code != 0) {
+                console.log('plugin not installed!');
+            } else {
+                installPlugin(platform, projectDir, tmpPluginDir);
+            }
+        });
+        process.on('exit', function(code) {
+            spawn('rm', ['-rf', tmpPluginDir]);
+        });
+    // or use local path
+    } else {
+        installPlugin(platform, projectDir, pluginDir);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..0789407
--- /dev/null
+++ b/package.json
@@ -0,0 +1,30 @@
+{
+  "author": "Andrew Lunny <al...@gmail.com>",
+  "name": "pluginstall",
+  "description": "install Cordova plugins",
+  "version": "0.3.4",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/alunny/pluginstall.git"
+  },
+  "main": "pluginstall.js",
+  "engines": {
+    "node": ">=0.6.7"
+  },
+  "dependencies": {
+     "elementtree":"0.1.1",
+     "rimraf":"1.0.9",
+     "ncp":"0.2.6",
+     "mkdirp":"0.2.2",
+     "xcode":"0.3.5",
+     "plist":"git+https://github.com/joshfire/node-plist.git",
+     "glob":"3.0.1"
+  },
+  "devDependencies": {
+    "nodeunit": "0.6.4"
+  },
+  "bin" : { "pluginstall" : "./cli.js" },
+  "scripts": {
+    "test": "node_modules/.bin/nodeunit test"
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/platforms.json
----------------------------------------------------------------------
diff --git a/platforms.json b/platforms.json
new file mode 100644
index 0000000..d9ccf5f
--- /dev/null
+++ b/platforms.json
@@ -0,0 +1 @@
+["android", "ios"]

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/platforms/android.js
----------------------------------------------------------------------
diff --git a/platforms/android.js b/platforms/android.js
new file mode 100644
index 0000000..3ab7a79
--- /dev/null
+++ b/platforms/android.js
@@ -0,0 +1,121 @@
+var fs = require('fs'),
+    path = require('path'),
+    mkdirp = require('mkdirp'),
+    et = require('elementtree'),
+    nCallbacks = require('../util/ncallbacks'),
+    asyncCopy = require('../util/asyncCopy'),
+    assetsDir = 'assets/www', // relative path to project's web assets
+    sourceDir = 'src',
+    counter = {};
+
+exports.installPlugin = function (config, plugin, callback) {
+    // look for assets in the plugin 
+    var assets = plugin.xmlDoc.findall('./asset'),
+        platformTag = plugin.xmlDoc.find('./platform[@name="android"]'),
+        sourceFiles = platformTag.findall('./source-file'),
+        pluginsChanges = platformTag.findall('./config-file[@target="res/xml/plugins.xml"]'),
+        manifestChanges = platformTag.findall('./config-file[@target="AndroidManifest.xml"]'),
+
+        callbackCount = assets.length + sourceFiles.length + pluginsChanges.length
+            + manifestChanges.length,
+        endCallback = nCallbacks(callbackCount, callback)
+
+    // move asset files
+    assets.forEach(function (asset) {
+        var srcPath = path.resolve(
+                        config.pluginPath,
+                        asset.attrib['src']);
+
+        var targetPath = path.resolve(
+                            config.projectPath,
+                            assetsDir,
+                            asset.attrib['target']);
+
+        asyncCopy(srcPath, targetPath, endCallback);
+    });
+
+    // move source files
+    sourceFiles.forEach(function (sourceFile) {
+        var srcDir = path.resolve(config.projectPath,
+                                sourceFile.attrib['target-dir'])
+
+        mkdirp(srcDir, function (err) {
+            var srcFile = path.resolve(config.pluginPath,
+                                        sourceFile.attrib['src']),
+                destFile = path.resolve(srcDir,
+                                path.basename(sourceFile.attrib['src']));
+
+            asyncCopy(srcFile, destFile, endCallback);
+        });
+    })
+
+    // edit plugins.xml
+    pluginsChanges.forEach(function (configNode) {
+        var pluginsPath = path.resolve(config.projectPath, 'res/xml/plugins.xml'),
+            pluginsDoc = readAsETSync(pluginsPath),
+            selector = configNode.attrib["parent"],
+            child = configNode.find('*');
+
+        if (addToDoc(pluginsDoc, child, selector)) {
+            fs.writeFile(pluginsPath, pluginsDoc.write(), function (err) {
+                if (err) endCallback(err);
+
+                endCallback();
+            });
+        } else {
+            endCallback('failed to add node to plugins.xml');
+        }
+    });
+
+    // edit AndroidManifest.xml
+    manifestChanges.forEach(function (configNode) {
+        var manifestPath = path.resolve(config.projectPath, 'AndroidManifest.xml'),
+            manifestDoc  = readAsETSync(manifestPath),
+            selector = configNode.attrib["parent"],
+            child = configNode.find('*');
+
+        if (addToDoc(manifestDoc, child, selector)) {
+            fs.writeFile(manifestPath, manifestDoc.write(), function (err) {
+                if (err) endCallback(err);
+
+                endCallback();
+            });
+        } else {
+            endCallback('failed to add node to AndroidManifest.xml')
+        }
+    })
+}
+
+// adds node to doc at selector
+function addToDoc(doc, node, selector) {
+    var ROOT = /^\/([^\/]*)/,
+        ABSOLUTE = /^\/([^\/]*)\/(.*)/,
+        parent, tagName, subSelector;
+
+    // handle absolute selector (which elementtree doesn't like)
+    if (ROOT.test(selector)) {
+        tagName = selector.match(ROOT)[1];
+        if (tagName === doc._root.tag) {
+            parent = doc._root;
+
+            // could be an absolute path, but not selecting the root
+            if (ABSOLUTE.test(selector)) {
+                subSelector = selector.match(ABSOLUTE)[2];
+                parent = parent.find(subSelector)
+            }
+        } else {
+            return false;
+        }
+    } else {
+        parent = doc.find(selector)
+    }
+
+    parent.append(node);
+    return true;
+}
+
+function readAsETSync(filename) {
+    var contents = fs.readFileSync(filename, 'utf-8');
+
+    return new et.ElementTree(et.XML(contents));
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/platforms/ios.js
----------------------------------------------------------------------
diff --git a/platforms/ios.js b/platforms/ios.js
new file mode 100644
index 0000000..b35c41d
--- /dev/null
+++ b/platforms/ios.js
@@ -0,0 +1,131 @@
+var path = require('path'),
+    fs = require('fs'),
+    glob = require('glob'),
+    xcode = require('xcode'),
+    plist = require('plist'),
+    nCallbacks = require('../util/ncallbacks'),
+    asyncCopy = require('../util/asyncCopy'),
+    assetsDir = 'www'; // relative path to project's web assets
+
+exports.installPlugin = function (config, plugin, callback) {
+    function prepare(then) {
+        var store = {},
+            end = nCallbacks(2, function (err) {
+                if (err) throw err;
+
+                else
+                    then(store.pbxPath, store.xcodeproj, store.plistPath,
+                        store.plist, store.pluginsDir);
+            });
+
+        // grab and parse pbxproj
+        glob(config.projectPath + '/**/project.pbxproj', function (err, files) {
+            if (!files.length) throw "does not appear to be an xcode project";
+
+            store.pbxPath = files[0];
+            store.xcodeproj = xcode.project(files[0]);
+            store.xcodeproj.parse(end);
+        });
+
+        // grab and parse plist file
+        glob(config.projectPath + '/**/{PhoneGap,Cordova}.plist', function (err, files) {
+            if (!files.length) throw "does not appear to be a PhoneGap project";
+
+            files = files.filter(function (val) {
+                return !(/^build\//.test(val))
+            });
+
+            store.plistPath = files[0];
+            store.pluginsDir = path.resolve(files[0], '..', 'Plugins');
+
+            plist.parseFile(store.plistPath, function (err, obj) {
+                store.plist = obj;
+                end();
+            });
+        });
+    }
+
+    prepare(function (pbxPath, xcodeproj, plistPath, plistObj, pluginsDir) {
+        var assets = plugin.xmlDoc.findall('./asset'),
+            platformTag = plugin.xmlDoc.find('./platform[@name="ios"]'),
+            sourceFiles = platformTag.findall('./source-file'),
+            headerFiles = platformTag.findall('./header-file'),
+            resourceFiles = platformTag.findall('./resource-file'),
+            frameworks = platformTag.findall('./framework'),
+            plistEle = platformTag.find('./plugins-plist'),
+
+            callbackCount = 0, end;
+
+        // callback for every file/dir to add
+        callbackCount += assets.length;
+        callbackCount += sourceFiles.length;
+        callbackCount += headerFiles.length;
+        callbackCount += resourceFiles.length;
+        // adding framework is sync, so don't add that
+        callbackCount++; // for writing the plist file
+        callbackCount++; // for writing the pbxproj file
+
+        end = nCallbacks(callbackCount, callback);
+
+        // move asset files into www
+        assets.forEach(function (asset) {
+            var srcPath = path.resolve(
+                            config.pluginPath, asset.attrib['src']);
+
+            var targetPath = path.resolve(
+                                config.projectPath,
+                                assetsDir, asset.attrib['target']);
+
+            asyncCopy(srcPath, targetPath, end);
+        });
+
+        // move native files (source/header/resource)
+        sourceFiles.forEach(function (sourceFile) {
+            var src = sourceFile.attrib['src'],
+                srcFile = path.resolve(config.pluginPath, 'src/ios', src),
+                destFile = path.resolve(pluginsDir, path.basename(src));
+
+            xcodeproj.addSourceFile('Plugins/' + path.basename(src));
+
+            asyncCopy(srcFile, destFile, end);
+        })
+
+        headerFiles.forEach(function (headerFile) {
+            var src = headerFile.attrib['src'],
+                srcFile = path.resolve(config.pluginPath, 'src/ios', src),
+                destFile = path.resolve(pluginsDir, path.basename(src));
+
+            xcodeproj.addHeaderFile('Plugins/' + path.basename(src));
+
+            asyncCopy(srcFile, destFile, end);
+        })
+
+        resourceFiles.forEach(function (resource) {
+            var src = resource.attrib['src'],
+                srcFile = path.resolve(config.pluginPath, 'src/ios', src),
+                destFile = path.resolve(pluginsDir, path.basename(src));
+
+            xcodeproj.addResourceFile('Plugins/' + path.basename(src));
+
+            asyncCopy(srcFile, destFile, end);
+        })
+
+        frameworks.forEach(function (framework) {
+            var src = framework.attrib['src'];
+
+            xcodeproj.addFramework(src);
+        });
+
+        // weirdness with node-plist and top-level <plist>
+        if (plistObj[0]) {
+            plistObj = plistObj[0];
+        }
+
+        // write out plist
+        plistObj.Plugins[plistEle.attrib['key']] = plistEle.attrib['string'];
+        fs.writeFile(plistPath, plist.stringify(plistObj), end);
+
+        // write out xcodeproj file
+        fs.writeFile(pbxPath, xcodeproj.writeSync(), end);
+    });
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/pluginstall.js
----------------------------------------------------------------------
diff --git a/pluginstall.js b/pluginstall.js
new file mode 100644
index 0000000..9d86f82
--- /dev/null
+++ b/pluginstall.js
@@ -0,0 +1,52 @@
+// copyright (c) 2012 Andrew Lunny, Adobe Systems
+var fs = require('fs'),
+    path = require('path'),
+    et = require('elementtree'),
+    platforms = require('./platforms'),
+    platformModules = {
+        'android': require('./platforms/android'),
+        'ios': require('./platforms/ios')
+    }
+
+// check arguments and resolve file paths
+exports.init = function (platform, projectPath, pluginPath) {
+    var projectPath = fs.realpathSync(projectPath),
+        pluginPath = fs.realpathSync(pluginPath);
+
+    if (platforms.indexOf(platform) < 0)
+        throw { name: "Platform Error", message: platform + " not supported" }
+
+    return {
+        platform:    platform,
+        projectPath: projectPath,
+        pluginPath:  pluginPath
+    }
+}
+
+exports.parseXml = function (config) {
+    var xmlPath     = path.join(config.pluginPath, 'plugin.xml'),
+        xmlText     = fs.readFileSync(xmlPath, 'utf-8'),
+        xmlDoc      = new et.ElementTree(et.XML(xmlText)),
+        rootAttr    = xmlDoc._root.attrib,
+        supportedPlatforms;
+
+    supportedPlatforms = xmlDoc.findall('platform').map(function (platform) {
+        return platform.attrib['name'];
+    });
+
+    return {
+        xmlDoc: xmlDoc,
+        _id: rootAttr['id'],
+        version: rootAttr['version'],
+        platforms: supportedPlatforms
+    };
+}
+
+// should move all asset and source files into the right places
+// and then edit all appropriate files (manifests and the like)
+exports.installPlugin = function (config, plugin, callback) {
+    // get the platform-specific fn
+    var platformInstall = platformModules[config.platform].installPlugin;
+
+    return platformInstall(config, plugin, callback)
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/android-install.js
----------------------------------------------------------------------
diff --git a/test/android-install.js b/test/android-install.js
new file mode 100644
index 0000000..0732616
--- /dev/null
+++ b/test/android-install.js
@@ -0,0 +1,115 @@
+    // core
+var fs = require('fs'),
+    path = require('path'),
+
+    // libs
+    rimraf = require('rimraf'),
+    et = require('elementtree'),
+
+    // parts of this lib
+    pluginstall = require('../pluginstall'),
+    android = require('../platforms/android'),
+    nCallbacks = require('../util/ncallbacks'),
+
+    // setup
+    config = {
+        platform: 'android',
+        projectPath: fs.realpathSync('test/project'),
+        pluginPath: fs.realpathSync('test/plugin')
+    },
+    plugin = pluginstall.parseXml(config),
+    assetsDir = path.resolve(config.projectPath, 'assets/www'),
+    srcDir = path.resolve(config.projectPath, 'src'),
+    jsPath = assetsDir + '/childbrowser.js',
+    assetPath = assetsDir + '/childbrowser',
+    javaDir  = path.resolve(config.projectPath,
+                            'src/com/phonegap/plugins/childBrowser'),
+    javaPath = path.resolve(javaDir, 'ChildBrowser.java')
+
+function moveProjFile(origFile, callback) {
+    var src = path.resolve(config.projectPath, origFile),
+        dest = src.replace('.orig', '')
+
+    fs.createReadStream(src)
+        .pipe(fs.createWriteStream(dest))
+        .on('close', callback);
+}
+
+// global setup
+exports.setUp = function (callback) {
+    var ASYNC_OPS = 5,
+        end = nCallbacks(ASYNC_OPS, callback);
+
+    // remove JS (that should be moved)
+    fs.stat(jsPath, function (err, stats) {
+        if (stats) {
+            fs.unlinkSync(jsPath)
+        }
+
+        end(null);
+    });
+
+    // remove web assets (www/childbrowser)
+    rimraf(assetPath, function () {
+        end(null)
+    });
+
+    // remove src code directory
+    rimraf(javaDir, function () {
+        end(null)
+    });
+
+    // copy in original plugins.xml
+    moveProjFile('res/xml/plugins.orig.xml', end)
+
+    // copy in original AndroidManifest.xml
+    moveProjFile('AndroidManifest.orig.xml', end)
+}
+
+exports['should move the js file'] = function (test) {
+    android.installPlugin(config, plugin, function (err) {
+        test.ok(fs.statSync(jsPath))
+        test.done();
+    })
+}
+
+exports['should move the directory'] = function (test) {
+    android.installPlugin(config, plugin, function (err) {
+        var assets = fs.statSync(assetPath);
+
+        test.ok(assets.isDirectory())
+        test.ok(fs.statSync(assetPath + '/image.jpg'))
+        test.done();
+    })
+}
+
+exports['should move the src file'] = function (test) {
+    android.installPlugin(config, plugin, function (err) {
+        test.ok(fs.statSync(javaPath))
+        test.done();
+    })
+}
+
+exports['should add ChildBrowser to plugins.xml'] = function (test) {
+    android.installPlugin(config, plugin, function (err) {
+        var pluginsTxt = fs.readFileSync('test/project/res/xml/plugins.xml', 'utf-8'),
+            pluginsDoc = new et.ElementTree(et.XML(pluginsTxt)),
+            expected = 'plugin[@name="ChildBrowser"]' +
+                        '[@value="com.phonegap.plugins.childBrowser.ChildBrowser"]';
+
+        test.ok(pluginsDoc.find(expected));
+        test.done();
+    })
+}
+
+exports['should add ChildBrowser to AndroidManifest.xml'] = function (test) {
+    android.installPlugin(config, plugin, function (err) {
+        var manifestTxt = fs.readFileSync('test/project/AndroidManifest.xml', 'utf-8'),
+            manifestDoc = new et.ElementTree(et.XML(manifestTxt)),
+            expected = 'application/activity[@android:name=' +
+                        '"com.phonegap.plugins.childBrowser.ChildBrowser"]';
+
+        test.ok(manifestDoc.find(expected));
+        test.done();
+    })
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/initialize.js
----------------------------------------------------------------------
diff --git a/test/initialize.js b/test/initialize.js
new file mode 100644
index 0000000..e72bb82
--- /dev/null
+++ b/test/initialize.js
@@ -0,0 +1,26 @@
+var pluginstall = require('../pluginstall');
+
+exports['should exist'] = function (test) {
+    test.equal(typeof pluginstall.init, 'function');
+    test.done();
+}
+
+exports['should return expected values'] = function (test) {
+    var initObj = pluginstall.init('android', '.', 'test/project'),
+        projPath = process.cwd(),
+        plugPath = projPath + '/test/project'
+
+    test.equal(initObj.platform, 'android');
+    test.equal(initObj.projectPath, projPath);
+    test.equal(initObj.pluginPath, plugPath);
+
+    test.done();
+}
+
+exports['should throw for unsupported platforms'] = function (test) {
+    test.throws(function () {
+        pluginstall.init('palm-treo', '.', 'test/project');
+    });
+
+    test.done();
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/ios-install.js
----------------------------------------------------------------------
diff --git a/test/ios-install.js b/test/ios-install.js
new file mode 100644
index 0000000..4d9b91c
--- /dev/null
+++ b/test/ios-install.js
@@ -0,0 +1,144 @@
+var fs = require('fs'),
+    path = require('path'),
+    rimraf = require('rimraf'),
+    plist = require('plist'),
+    xcode = require('xcode'),
+
+    pluginstall = require('../pluginstall'),
+    ios = require('../platforms/ios'),
+    nCallbacks = require('../util/ncallbacks'),
+
+    config = {
+        platform: 'ios',
+        projectPath: fs.realpathSync('test/project'),
+        pluginPath: fs.realpathSync('test/plugin')
+    },
+    plugin = pluginstall.parseXml(config),
+    assetsDir = path.resolve(config.projectPath, 'www'),
+    srcDir = path.resolve(config.projectPath, 'SampleApp/Plugins'),
+    jsPath = assetsDir + '/childbrowser.js';
+
+function moveProjFile(origFile, callback) {
+    var src = path.resolve(config.projectPath, origFile),
+        dest = src.replace('.orig', '')
+
+    fs.createReadStream(src)
+        .pipe(fs.createWriteStream(dest))
+        .on('close', callback);
+}
+
+function unlinkIfThere(filepath, cb) {
+    fs.stat(filepath, function (err, stat) {
+        if (err) {
+            cb(null);
+            return;
+        }
+
+        if (stat)
+            fs.unlinkSync(filepath);
+
+        cb(null);
+    })
+}
+
+exports.setUp = function (calllback) {
+    var ASYNC_OPS = 10,
+        end = nCallbacks(ASYNC_OPS, calllback);
+
+    rimraf(assetsDir + '/childbrowser', end)
+    rimraf(srcDir + '/ChildBrowser.bundle', end)
+    unlinkIfThere(jsPath, end)
+    unlinkIfThere(srcDir + '/ChildBrowserCommand.m', end)
+    unlinkIfThere(srcDir + '/ChildBrowserViewController.m', end)
+    unlinkIfThere(srcDir + '/ChildBrowserCommand.h', end)
+    unlinkIfThere(srcDir + '/ChildBrowserViewController.h', end)
+    unlinkIfThere(srcDir + '/ChildBrowserViewController.xib', end)
+
+    moveProjFile('SampleApp/PhoneGap.orig.plist', end);
+    moveProjFile('SampleApp.xcodeproj/project.orig.pbxproj', end);
+}
+
+exports['should move the js file'] = function (test) {
+    ios.installPlugin(config, plugin, function (err) {
+        test.ok(fs.statSync(jsPath))
+        test.done();
+    })
+}
+
+exports['should move the source files'] = function (test) {
+    ios.installPlugin(config, plugin, function (err) {
+        test.ok(fs.statSync(srcDir + '/ChildBrowserCommand.m'))
+        test.ok(fs.statSync(srcDir + '/ChildBrowserViewController.m'))
+        test.done();
+    })
+}
+
+exports['should move the header files'] = function (test) {
+    ios.installPlugin(config, plugin, function (err) {
+        test.ok(fs.statSync(srcDir + '/ChildBrowserCommand.h'))
+        test.ok(fs.statSync(srcDir + '/ChildBrowserViewController.h'))
+        test.done();
+    })
+}
+
+exports['should move the xib file'] = function (test) {
+    ios.installPlugin(config, plugin, function (err) {
+        test.ok(fs.statSync(srcDir + '/ChildBrowserViewController.xib'))
+        test.done();
+    })
+}
+
+exports['should move the bundle'] = function (test) {
+    ios.installPlugin(config, plugin, function (err) {
+        var bundle = fs.statSync(srcDir + '/ChildBrowser.bundle');
+
+        test.ok(bundle.isDirectory())
+        test.done();
+    })
+}
+
+exports['should edit PhoneGap.plist'] = function (test) {
+    ios.installPlugin(config, plugin, function (err) {
+        var plistPath = config.projectPath + '/SampleApp/PhoneGap.plist';
+        plist.parseFile(plistPath, function (err, obj) {
+
+            test.equal(obj.Plugins['com.phonegap.plugins.childbrowser'],
+                'ChildBrowserCommand');
+
+            test.done();
+        });
+    })
+}
+
+exports['should edit the pbxproj file'] = function (test) {
+    ios.installPlugin(config, plugin, function (err) {
+        var projPath = config.projectPath + '/SampleApp.xcodeproj/project.pbxproj';
+
+        xcode.project(projPath).parse(function (err, obj) {
+            var fileRefSection = obj.project.objects['PBXFileReference'],
+                fileRefLength = Object.keys(fileRefSection).length,
+                EXPECTED_TOTAL_REFERENCES = 84; // magic number ahoy!
+
+            test.equal(fileRefLength, EXPECTED_TOTAL_REFERENCES);
+            test.done();
+        })
+    });
+}
+
+exports['should add the framework references to the pbxproj file'] = function (test) {
+    ios.installPlugin(config, plugin, function (err) {
+        var projPath = config.projectPath + '/SampleApp.xcodeproj/project.pbxproj',
+            projContents = fs.readFileSync(projPath, 'utf8'),
+            projLines = projContents.split("\n"),
+            references;
+
+        references = projLines.filter(function (line) {
+            return !!(line.match("libsqlite3.dylib"));
+        })
+
+        // should be four libsqlite3 reference lines added
+        // pretty low-rent test eh
+        test.equal(references.length, 4);
+        test.done();
+    });
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/parseXml.js
----------------------------------------------------------------------
diff --git a/test/parseXml.js b/test/parseXml.js
new file mode 100644
index 0000000..abe8a0e
--- /dev/null
+++ b/test/parseXml.js
@@ -0,0 +1,31 @@
+var pluginstall = require('../pluginstall'),
+    fs = require('fs'),
+    et = require('elementtree'),
+    configObj = {
+        platform: 'android',
+        projectPath: fs.realpathSync('test/project'),
+        pluginPath: fs.realpathSync('test/plugin')
+    };
+
+exports['should exist'] = function (test) {
+    test.equals(typeof pluginstall.parseXml, 'function');
+    test.done();
+}
+
+exports['should return an object with an xmlDoc'] = function (test) {
+    var dataObj = pluginstall.parseXml(configObj);
+
+    test.equals(et.ElementTree, dataObj.xmlDoc.constructor);
+    test.done();
+}
+
+exports['should return an object with the correct fields'] = function (test) {
+    var dataObj = pluginstall.parseXml(configObj);
+
+    test.equals('com.phonegap.plugins.childbrowser', dataObj._id);
+    test.equals('0.6.0', dataObj.version);
+    test.equals(2, dataObj.platforms.length);
+    test.equals('android', dataObj.platforms[0]);
+
+    test.done();
+}

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/.gitkeep
----------------------------------------------------------------------
diff --git a/test/plugin/.gitkeep b/test/plugin/.gitkeep
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/plugin.xml
----------------------------------------------------------------------
diff --git a/test/plugin/plugin.xml b/test/plugin/plugin.xml
new file mode 100644
index 0000000..c5b18f1
--- /dev/null
+++ b/test/plugin/plugin.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    id="com.phonegap.plugins.childbrowser"
+    version="0.6.0">
+
+    <name>Child Browser</name>
+
+    <asset src="www/childbrowser.js" target="childbrowser.js" />
+    <asset src="www/childbrowser" target="childbrowser" />
+
+    <!-- android -->
+    <platform name="android">
+        <config-file target="AndroidManifest.xml" parent="/manifest/application">
+            <activity android:name="com.phonegap.plugins.childBrowser.ChildBrowser"
+                      android:label="@string/app_name">
+                <intent-filter>
+                </intent-filter>
+            </activity>
+        </config-file>
+
+        <config-file target="res/xml/plugins.xml" parent="/plugins">
+            <plugin name="ChildBrowser"
+                value="com.phonegap.plugins.childBrowser.ChildBrowser"/>
+        </config-file>
+
+        <source-file src="src/android/ChildBrowser.java"
+                target-dir="src/com/phonegap/plugins/childBrowser" />
+    </platform>
+
+    <!-- ios -->
+    <platform name="ios">
+        <plugins-plist key="com.phonegap.plugins.childbrowser"
+            string="ChildBrowserCommand" />
+
+        <resource-file src="ChildBrowser.bundle" />
+        <resource-file src="ChildBrowserViewController.xib" />
+
+        <header-file src="ChildBrowserCommand.h" />
+        <header-file src="ChildBrowserViewController.h" />
+
+        <source-file src="ChildBrowserCommand.m" />
+        <source-file src="ChildBrowserViewController.m" />
+
+        <!-- framework for testing (not actual dependency of ChildBrowser -->
+        <framework src="libsqlite3.dylib" />
+    </platform>
+</plugin>

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/android/ChildBrowser.java
----------------------------------------------------------------------
diff --git a/test/plugin/src/android/ChildBrowser.java b/test/plugin/src/android/ChildBrowser.java
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/ios/ChildBrowser.bundle/arrow_left.png
----------------------------------------------------------------------
diff --git a/test/plugin/src/ios/ChildBrowser.bundle/arrow_left.png b/test/plugin/src/ios/ChildBrowser.bundle/arrow_left.png
new file mode 100644
index 0000000..530e12b
Binary files /dev/null and b/test/plugin/src/ios/ChildBrowser.bundle/arrow_left.png differ

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/ios/ChildBrowser.bundle/arrow_left@2x.png
----------------------------------------------------------------------
diff --git a/test/plugin/src/ios/ChildBrowser.bundle/arrow_left@2x.png b/test/plugin/src/ios/ChildBrowser.bundle/arrow_left@2x.png
new file mode 100644
index 0000000..530e12b
Binary files /dev/null and b/test/plugin/src/ios/ChildBrowser.bundle/arrow_left@2x.png differ

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/ios/ChildBrowser.bundle/arrow_right.png
----------------------------------------------------------------------
diff --git a/test/plugin/src/ios/ChildBrowser.bundle/arrow_right.png b/test/plugin/src/ios/ChildBrowser.bundle/arrow_right.png
new file mode 100644
index 0000000..8b3d855
Binary files /dev/null and b/test/plugin/src/ios/ChildBrowser.bundle/arrow_right.png differ

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/ios/ChildBrowser.bundle/arrow_right@2x.png
----------------------------------------------------------------------
diff --git a/test/plugin/src/ios/ChildBrowser.bundle/arrow_right@2x.png b/test/plugin/src/ios/ChildBrowser.bundle/arrow_right@2x.png
new file mode 100644
index 0000000..8b3d855
Binary files /dev/null and b/test/plugin/src/ios/ChildBrowser.bundle/arrow_right@2x.png differ

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/ios/ChildBrowser.bundle/but_refresh.png
----------------------------------------------------------------------
diff --git a/test/plugin/src/ios/ChildBrowser.bundle/but_refresh.png b/test/plugin/src/ios/ChildBrowser.bundle/but_refresh.png
new file mode 100644
index 0000000..309b6bd
Binary files /dev/null and b/test/plugin/src/ios/ChildBrowser.bundle/but_refresh.png differ

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/ios/ChildBrowser.bundle/but_refresh@2x.png
----------------------------------------------------------------------
diff --git a/test/plugin/src/ios/ChildBrowser.bundle/but_refresh@2x.png b/test/plugin/src/ios/ChildBrowser.bundle/but_refresh@2x.png
new file mode 100644
index 0000000..309b6bd
Binary files /dev/null and b/test/plugin/src/ios/ChildBrowser.bundle/but_refresh@2x.png differ

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/ios/ChildBrowser.bundle/compass.png
----------------------------------------------------------------------
diff --git a/test/plugin/src/ios/ChildBrowser.bundle/compass.png b/test/plugin/src/ios/ChildBrowser.bundle/compass.png
new file mode 100644
index 0000000..46a8901
Binary files /dev/null and b/test/plugin/src/ios/ChildBrowser.bundle/compass.png differ

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/ios/ChildBrowser.bundle/compass@2x.png
----------------------------------------------------------------------
diff --git a/test/plugin/src/ios/ChildBrowser.bundle/compass@2x.png b/test/plugin/src/ios/ChildBrowser.bundle/compass@2x.png
new file mode 100644
index 0000000..46a8901
Binary files /dev/null and b/test/plugin/src/ios/ChildBrowser.bundle/compass@2x.png differ

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/ios/ChildBrowserCommand.h
----------------------------------------------------------------------
diff --git a/test/plugin/src/ios/ChildBrowserCommand.h b/test/plugin/src/ios/ChildBrowserCommand.h
new file mode 100644
index 0000000..5e86a67
--- /dev/null
+++ b/test/plugin/src/ios/ChildBrowserCommand.h
@@ -0,0 +1,30 @@
+//
+//  PhoneGap ! ChildBrowserCommand
+//
+//
+//  Created by Jesse MacFadyen on 10-05-29.
+//  Copyright 2010 Nitobi. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#ifdef PHONEGAP_FRAMEWORK
+	#import <PhoneGap/PGPlugin.h>
+#else
+	#import "PGPlugin.h"
+#endif
+#import "ChildBrowserViewController.h"
+
+
+
+@interface ChildBrowserCommand : PGPlugin <ChildBrowserDelegate>  {
+
+	ChildBrowserViewController* childBrowser;
+}
+
+@property (nonatomic, retain) ChildBrowserViewController *childBrowser;
+
+
+- (void) showWebPage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+-(void) onChildLocationChange:(NSString*)newLoc;
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/ios/ChildBrowserCommand.m
----------------------------------------------------------------------
diff --git a/test/plugin/src/ios/ChildBrowserCommand.m b/test/plugin/src/ios/ChildBrowserCommand.m
new file mode 100644
index 0000000..38aaf64
--- /dev/null
+++ b/test/plugin/src/ios/ChildBrowserCommand.m
@@ -0,0 +1,86 @@
+//
+
+// 
+//
+//  Created by Jesse MacFadyen on 10-05-29.
+//  Copyright 2010 Nitobi. All rights reserved.
+//  Copyright (c) 2011, IBM Corporation
+//  Copyright 2011, Randy McMillan
+//
+
+#import "ChildBrowserCommand.h"
+
+#ifdef PHONEGAP_FRAMEWORK
+	#import <PhoneGap/PhoneGapViewController.h>
+#else
+	#import "PhoneGapViewController.h"
+#endif
+
+
+@implementation ChildBrowserCommand
+
+@synthesize childBrowser;
+
+- (void) showWebPage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options // args: url
+{	
+	
+    if(childBrowser == NULL)
+	{
+		childBrowser = [[ ChildBrowserViewController alloc ] initWithScale:FALSE ];
+		childBrowser.delegate = self;
+	}
+	
+/* // TODO: Work in progress
+	NSString* strOrientations = [ options objectForKey:@"supportedOrientations"];
+	NSArray* supportedOrientations = [strOrientations componentsSeparatedByString:@","];
+*/
+    PhoneGapViewController* cont = (PhoneGapViewController*)[ super appViewController ];
+    childBrowser.supportedOrientations = cont.supportedOrientations;
+    
+    if ([cont respondsToSelector:@selector(presentViewController)]) {
+        //Reference UIViewController.h Line:179 for update to iOS 5 difference - @RandyMcMillan
+        [cont presentViewController:childBrowser animated:YES completion:nil];        
+    } else {
+        [ cont presentModalViewController:childBrowser animated:YES ];
+    }                 
+        
+    NSString *url = (NSString*) [arguments objectAtIndex:0];
+        
+    [childBrowser loadURL:url  ];
+        
+}
+
+-(void) close:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options // args: url
+{
+    [ childBrowser closeBrowser];
+	
+}
+
+-(void) onClose
+{
+	NSString* jsCallback = [NSString stringWithFormat:@"ChildBrowser._onClose();",@""];
+	[self.webView stringByEvaluatingJavaScriptFromString:jsCallback];
+}
+
+-(void) onOpenInSafari
+{
+	NSString* jsCallback = [NSString stringWithFormat:@"ChildBrowser._onOpenExternal();",@""];
+	[self.webView stringByEvaluatingJavaScriptFromString:jsCallback];
+}
+
+
+-(void) onChildLocationChange:(NSString*)newLoc
+{
+	
+	NSString* tempLoc = [NSString stringWithFormat:@"%@",newLoc];
+	NSString* encUrl = [tempLoc stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+	 
+	NSString* jsCallback = [NSString stringWithFormat:@"ChildBrowser._onLocationChange('%@');",encUrl];
+	[self.webView stringByEvaluatingJavaScriptFromString:jsCallback];
+
+}
+
+
+
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/ios/ChildBrowserViewController.h
----------------------------------------------------------------------
diff --git a/test/plugin/src/ios/ChildBrowserViewController.h b/test/plugin/src/ios/ChildBrowserViewController.h
new file mode 100644
index 0000000..8767614
--- /dev/null
+++ b/test/plugin/src/ios/ChildBrowserViewController.h
@@ -0,0 +1,54 @@
+//
+//  ChildBrowserViewController.h
+//
+//  Created by Jesse MacFadyen on 21/07/09.
+//  Copyright 2009 Nitobi. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@protocol ChildBrowserDelegate<NSObject>
+
+
+
+/*
+ *  onChildLocationChanging:newLoc
+ *  
+ *  Discussion:
+ *    Invoked when a new page has loaded
+ */
+-(void) onChildLocationChange:(NSString*)newLoc;
+-(void) onOpenInSafari;
+-(void) onClose;
+@end
+
+
+@interface ChildBrowserViewController : UIViewController < UIWebViewDelegate > {
+	IBOutlet UIWebView* webView;
+	IBOutlet UIBarButtonItem* closeBtn;
+	IBOutlet UIBarButtonItem* refreshBtn;
+	IBOutlet UILabel* addressLabel;
+	IBOutlet UIBarButtonItem* backBtn;
+	IBOutlet UIBarButtonItem* fwdBtn;
+	IBOutlet UIBarButtonItem* safariBtn;
+	IBOutlet UIActivityIndicatorView* spinner;
+	BOOL scaleEnabled;
+	BOOL isImage;
+	NSString* imageURL;
+	NSArray* supportedOrientations;
+	id <ChildBrowserDelegate> delegate;
+}
+
+@property (nonatomic, retain)id <ChildBrowserDelegate> delegate;
+@property (nonatomic, retain) 	NSArray* supportedOrientations;
+@property(retain) NSString* imageURL;
+@property(assign) BOOL isImage;
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation; 
+- (ChildBrowserViewController*)initWithScale:(BOOL)enabled;
+- (IBAction)onDoneButtonPress:(id)sender;
+- (IBAction)onSafariButtonPress:(id)sender;
+- (void)loadURL:(NSString*)url;
+-(void)closeBrowser;
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-cordova-labs/blob/4f8ab9d0/test/plugin/src/ios/ChildBrowserViewController.m
----------------------------------------------------------------------
diff --git a/test/plugin/src/ios/ChildBrowserViewController.m b/test/plugin/src/ios/ChildBrowserViewController.m
new file mode 100644
index 0000000..167ef98
--- /dev/null
+++ b/test/plugin/src/ios/ChildBrowserViewController.m
@@ -0,0 +1,239 @@
+//
+//  ChildBrowserViewController.m
+//
+//  Created by Jesse MacFadyen on 21/07/09.
+//  Copyright 2009 Nitobi. All rights reserved.
+//  Copyright (c) 2011, IBM Corporation
+//  Copyright 2011, Randy McMillan
+//
+
+#import "ChildBrowserViewController.h"
+
+
+@implementation ChildBrowserViewController
+
+@synthesize imageURL;
+@synthesize supportedOrientations;
+@synthesize isImage;
+@synthesize delegate;
+
+/*
+ // The designated initializer.  Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
+- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
+    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
+        // Custom initialization
+    }
+    return self;
+}
+*/
+
++ (NSString*) resolveImageResource:(NSString*)resource
+{
+	NSString* systemVersion = [[UIDevice currentDevice] systemVersion];
+	BOOL isLessThaniOS4 = ([systemVersion compare:@"4.0" options:NSNumericSearch] == NSOrderedAscending);
+	
+	// the iPad image (nor retina) differentiation code was not in 3.x, and we have to explicitly set the path
+	if (isLessThaniOS4)
+	{
+        return [NSString stringWithFormat:@"%@.png", resource];
+	}
+	
+	return resource;
+}
+
+
+- (ChildBrowserViewController*)initWithScale:(BOOL)enabled
+{
+    self = [super init];
+	
+	
+	scaleEnabled = enabled;
+	
+	return self;	
+}
+
+// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    
+	refreshBtn.image = [UIImage imageNamed:[[self class] resolveImageResource:@"ChildBrowser.bundle/but_refresh"]];
+	backBtn.image = [UIImage imageNamed:[[self class] resolveImageResource:@"ChildBrowser.bundle/arrow_left"]];
+	fwdBtn.image = [UIImage imageNamed:[[self class] resolveImageResource:@"ChildBrowser.bundle/arrow_right"]];
+	safariBtn.image = [UIImage imageNamed:[[self class] resolveImageResource:@"ChildBrowser.bundle/compass"]];
+
+	webView.delegate = self;
+	webView.scalesPageToFit = TRUE;
+	webView.backgroundColor = [UIColor whiteColor];
+	NSLog(@"View did load");
+}
+
+
+
+
+
+- (void)didReceiveMemoryWarning {
+	// Releases the view if it doesn't have a superview.
+    [super didReceiveMemoryWarning];
+	
+	// Release any cached data, images, etc that aren't in use.
+}
+
+- (void)viewDidUnload {
+	// Release any retained subviews of the main view.
+	// e.g. self.myOutlet = nil;
+	NSLog(@"View did UN-load");
+}
+
+
+- (void)dealloc {
+
+	webView.delegate = nil;
+	
+	[webView release];
+	[closeBtn release];
+	[refreshBtn release];
+	[addressLabel release];
+	[backBtn release];
+	[fwdBtn release];
+	[safariBtn release];
+	[spinner release];
+	[ supportedOrientations release];
+	[super dealloc];
+}
+
+-(void)closeBrowser
+{
+	
+	if(delegate != NULL)
+	{
+		[delegate onClose];		
+	}
+    if ([self respondsToSelector:@selector(presentingViewController)]) { 
+        //Reference UIViewController.h Line:179 for update to iOS 5 difference - @RandyMcMillan
+        [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
+    } else {
+        [[self parentViewController] dismissModalViewControllerAnimated:YES];
+    }
+}
+
+-(IBAction) onDoneButtonPress:(id)sender
+{
+	[ self closeBrowser];
+
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]];
+    [webView loadRequest:request];
+}
+
+
+-(IBAction) onSafariButtonPress:(id)sender
+{
+	
+	if(delegate != NULL)
+	{
+		[delegate onOpenInSafari];		
+	}
+	
+	if(isImage)
+	{
+		NSURL* pURL = [[ [NSURL alloc] initWithString:imageURL ] autorelease];
+		[ [ UIApplication sharedApplication ] openURL:pURL  ];
+	}
+	else
+	{
+		NSURLRequest *request = webView.request;
+		[[UIApplication sharedApplication] openURL:request.URL];
+	}
+
+	 
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation) interfaceOrientation 
+{
+	BOOL autoRotate = [self.supportedOrientations count] > 1; // autorotate if only more than 1 orientation supported
+	if (autoRotate)
+	{
+		if ([self.supportedOrientations containsObject:
+			 [NSNumber numberWithInt:interfaceOrientation]]) {
+			return YES;
+		}
+    }
+	
+	return NO;
+}
+
+
+
+
+- (void)loadURL:(NSString*)url
+{
+	NSLog(@"Opening Url : %@",url);
+	 
+	if( [url hasSuffix:@".png" ]  || 
+	    [url hasSuffix:@".jpg" ]  || 
+		[url hasSuffix:@".jpeg" ] || 
+		[url hasSuffix:@".bmp" ]  || 
+		[url hasSuffix:@".gif" ]  )
+	{
+		[ imageURL release ];
+		imageURL = [url copy];
+		isImage = YES;
+		NSString* htmlText = @"<html><body style='background-color:#333;margin:0px;padding:0px;'><img style='min-height:200px;margin:0px;padding:0px;width:100%;height:auto;' alt='' src='IMGSRC'/></body></html>";
+		htmlText = [ htmlText stringByReplacingOccurrencesOfString:@"IMGSRC" withString:url ];
+
+		[webView loadHTMLString:htmlText baseURL:[NSURL URLWithString:@""]];
+		
+	}
+	else
+	{
+		imageURL = @"";
+		isImage = NO;
+		NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
+		[webView loadRequest:request];
+	}
+	webView.hidden = NO;
+}
+
+
+- (void)webViewDidStartLoad:(UIWebView *)sender {
+	addressLabel.text = @"Loading...";
+	backBtn.enabled = webView.canGoBack;
+	fwdBtn.enabled = webView.canGoForward;
+	
+	[ spinner startAnimating ];
+	
+}
+
+- (void)webViewDidFinishLoad:(UIWebView *)sender 
+{
+	NSURLRequest *request = webView.request;
+	NSLog(@"New Address is : %@",request.URL.absoluteString);
+	addressLabel.text = request.URL.absoluteString;
+	backBtn.enabled = webView.canGoBack;
+	fwdBtn.enabled = webView.canGoForward;
+	[ spinner stopAnimating ];
+	
+	if(delegate != NULL)
+	{
+		[delegate onChildLocationChange:request.URL.absoluteString];		
+	}
+
+}
+
+- (void)webView:(UIWebView *)wv didFailLoadWithError:(NSError *)error {
+    NSLog (@"webView:didFailLoadWithError");
+    [spinner stopAnimating];
+    addressLabel.text = @"Failed";
+    if (error != NULL) {
+        UIAlertView *errorAlert = [[UIAlertView alloc]
+                                   initWithTitle: [error localizedDescription]
+                                   message: [error localizedFailureReason]
+                                   delegate:nil
+                                   cancelButtonTitle:@"OK"
+                                   otherButtonTitles:nil];
+        [errorAlert show];
+        [errorAlert release];
+    }
+}
+
+
+@end