You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ni...@apache.org on 2015/05/13 23:14:57 UTC

[02/14] cordova-windows git commit: CB-8923 Add support for Windows 10 platform

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/0d2387d9/template/cordova/lib/prepare.js
----------------------------------------------------------------------
diff --git a/template/cordova/lib/prepare.js b/template/cordova/lib/prepare.js
index 7bec13d..4a02c12 100644
--- a/template/cordova/lib/prepare.js
+++ b/template/cordova/lib/prepare.js
@@ -17,43 +17,74 @@
        under the License.
 */
 
-var path  = require('path'),
-    fs  = require('fs'),
-    et = require('elementtree'),
-    shell = require('shelljs'),
-    ConfigParser = require('./ConfigParser');
+var path            = require('path'),
+    fs              = require('fs'),
+    et              = require('elementtree'),
+    subElement      = et.SubElement,
+    shell           = require('shelljs'),
+    MSBuildTools    = require('./MSBuildTools'),
+    Version         = require('./Version'),
+    ConfigParser    = require('./ConfigParser');
 
 var ROOT = path.join(__dirname, '..', '..'),
-    accessRules;
+    PROJECT_WINDOWS10   = 'CordovaApp.Windows10.jsproj',
+    MANIFEST_WINDOWS8   = 'package.windows80.appxmanifest',
+    MANIFEST_WINDOWS    = 'package.windows.appxmanifest',
+    MANIFEST_PHONE      = 'package.phone.appxmanifest',
+    MANIFEST_WINDOWS10  = 'package.windows10.appxmanifest',
+    JSPROJ_WINDOWS8     = 'CordovaApp.Windows80.jsproj',
+    JSPROJ_WINDOWS      = 'CordovaApp.Windows.jsproj',
+    JSPROJ_PHONE        = 'CordovaApp.Phone.jsproj',
+    JSPROJ_WINDOWS10    = 'CordovaApp.Windows10.jsproj',
+    BASE_UAP_VERSION    = new Version(10, 0, 10030, 0),
+    UAP_RESTRICTED_CAPS = ['enterpriseAuthentication', 'sharedUserCertificates', 
+                           'documentsLibrary', 'musicLibrary', 'picturesLibrary', 
+                           'videosLibrary', 'removableStorage', 'internetClientClientServer', 
+                           'privateNetworkClientServer'],
+    // UAP namespace capabilities come from the XSD type ST_Capability_Uap from AppxManifestTypes.xsd
+    CAPS_NEEDING_UAPNS  = ['documentsLibrary', 'picturesLibrary', 'videosLibrary', 
+                           'musicLibrary', 'enterpriseAuthentication', 'sharedUserCertificates', 
+                           'removableStorage', 'appointments', 'contacts', 'userAccountInformation',
+                           'phoneCall', 'blockedChatMessages', 'objects3D'];
 
 module.exports.applyPlatformConfig = function () {
     console.log('Applying Platform Config...');
 
     var config = new ConfigParser(path.join(ROOT, 'config.xml'));
 
-    accessRules = config.getAccessRules().filter(function(rule) {
-        if (rule.indexOf('https://') === 0 || rule == '*') {
-            return true;
-        } else {
-            console.log('Access rules must begin with "https://", the following rule will be ignored: ' + rule);
-        }
-        return false;
-    });
+    var isTargetingWin10 = false;
+    var winTargetVersion = config.getWindowsTargetVersion();
+    if (winTargetVersion === '10.0' || winTargetVersion === 'UAP') {
+        isTargetingWin10 = true;
+    }
 
     // Apply appxmanifest changes
-    [{ fileName: 'package.windows.appxmanifest',   namespacePrefix: 'm2:' }, 
-     { fileName: 'package.windows80.appxmanifest', namespacePrefix: '' },
-     { fileName: 'package.phone.appxmanifest',     namespacePrefix: 'm3:' }].forEach(
+    [{ fileName: MANIFEST_WINDOWS,   namespacePrefix: 'm2:' }, 
+     { fileName: MANIFEST_WINDOWS8,  namespacePrefix: '' },
+     { fileName: MANIFEST_PHONE,     namespacePrefix: 'm3:' }].forEach(
         function(manifestFile) {
-            updateManifestFile(config, path.join(ROOT, manifestFile.fileName), manifestFile.namespacePrefix);
+            updateManifestFile(config, path.join(ROOT, manifestFile.fileName), manifestFile.namespacePrefix, null);
     });
 
     // Apply jsproj changes
-    ['CordovaApp.Phone.jsproj', 'CordovaApp.Windows.jsproj', 'CordovaApp.Windows80.jsproj'].forEach(
+    [JSPROJ_PHONE, JSPROJ_WINDOWS, JSPROJ_WINDOWS8, JSPROJ_WINDOWS10].forEach(
         function(jsprojFile) {
             updatejsprojFile(config, path.join(ROOT, jsprojFile));
     });
 
+    // Break out Windows 10-specific functionality because we also need to 
+    // apply UAP versioning to Windows 10 appx-manifests.
+    var uapVersionInfo = getUAPVersions(config);
+
+    if (uapVersionInfo) {
+        updateManifestFile(config, path.join(ROOT, MANIFEST_WINDOWS10), 'uap:', uapVersionInfo);
+
+        applyUAPVersionToProject(path.join(ROOT, PROJECT_WINDOWS10), uapVersionInfo);
+    }
+    else if (isTargetingWin10) {
+        console.warn('Warning: Requested build targeting Windows 10, but Windows 10 tools were not detected on the system.');
+    }
+
     copyImages(config);
 };
 
@@ -71,8 +102,7 @@ function updatejsprojFile(config, jsProjFilePath) {
     fs.writeFileSync(jsProjFilePath, jsProjXML.write({indent: 2}), 'utf-8');
 }
 
-function updateManifestFile (config, manifestPath, namespacePrefix) {
-
+function updateManifestFile (config, manifestPath, namespacePrefix, uapVersionInfo) {
     var contents = fs.readFileSync(manifestPath, 'utf-8');
     if(contents) {
         //Windows is the BOM. Skip the Byte Order Mark.
@@ -81,29 +111,27 @@ function updateManifestFile (config, manifestPath, namespacePrefix) {
 
     var manifest =  new et.ElementTree(et.XML(contents));
 
-    applyCoreProperties(config, manifest, manifestPath, namespacePrefix);
+    applyCoreProperties(config, manifest, manifestPath, namespacePrefix, !!uapVersionInfo); 
     // sort Capability elements as per CB-5350 Windows8 build fails due to invalid 'Capabilities' definition
     sortCapabilities(manifest);
-    applyAccessRules(config, manifest);
+    applyAccessRules(config, manifest, !!uapVersionInfo); 
     applyBackgroundColor(config, manifest, namespacePrefix);
 
+    if (uapVersionInfo) {
+        applyTargetPlatformVersion(config, manifest, uapVersionInfo);
+        checkForRestrictedCapabilities(config, manifest);
+        ensureUapPrefixedCapabilities(manifest.find('.//Capabilities'));
+    }
 
     //Write out manifest
     fs.writeFileSync(manifestPath, manifest.write({indent: 4}), 'utf-8');
 }
 
-function applyCoreProperties(config, manifest, manifestPath, xmlnsPrefix) {
+function applyCoreProperties(config, manifest, manifestPath, xmlnsPrefix, targetWin10) {
     var version = fixConfigVersion(config.version());
     var name = config.name();
     var pkgName = config.packageName();
     var author = config.author();
-    var startPage = config.startPage();
-
-    if (!startPage) {
-        // If not specified, set default value
-        // http://cordova.apache.org/docs/en/edge/config_ref_index.md.html#The%20config.xml%20File
-        startPage = 'index.html';
-    }
 
     var identityNode = manifest.find('.//Identity');
     if(!identityNode) {
@@ -128,7 +156,8 @@ function applyCoreProperties(config, manifest, manifestPath, xmlnsPrefix) {
         var appId = pkgName.length <= 64 ? pkgName : pkgName.substr(0, 64);
         app.attrib.Id = appId;
     }
-    app.attrib.StartPage = 'www/' + startPage;
+
+    applyStartPage(app, config, targetWin10); 
 
     var visualElementsName = './/' + xmlnsPrefix + 'VisualElements'; 
     var visualElems = manifest.find(visualElementsName); 
@@ -200,6 +229,46 @@ function applyCoreProperties(config, manifest, manifestPath, xmlnsPrefix) {
     }
 }
 
+function applyStartPage(appNode, config, targetingWin10) {
+    var startPage = config.startPage();
+
+    if (!startPage) {
+        // If not specified, set default value
+        // http://cordova.apache.org/docs/en/edge/config_ref_index.md.html#The%20config.xml%20File
+        startPage = 'index.html';
+    }
+
+    var uriPrefix = '';
+    if (targetingWin10) {
+        // for Win10, we respect config options such as WindowsDefaultUriPrefix and default to 
+        // ms-appx-web:// as the homepage.  Set those here.
+
+        // Only add a URI prefix if the start page doesn't specify a URI scheme
+        if (!(/^[\w-]+?\:\/\//i).test(startPage)) {
+            uriPrefix = config.getPreference('WindowsDefaultUriPrefix');
+            if (!uriPrefix) {
+                uriPrefix = 'ms-appx-web://';
+            }
+            else if (/^ms\-appx\:\/\/$/i.test(uriPrefix)) { 
+                // Explicitly ignore the ms-appx:// scheme because it doesn't validate
+                // in the Windows 10 build schema (treat it as the root).
+                uriPrefix = '';
+            }
+        }
+    }
+
+    var startPagePrefix = 'www/';
+    if ((uriPrefix && uriPrefix.toLowerCase().substring(0, 4) === 'http') || 
+        startPage.toLowerCase().substring(0, 4) === 'http') {
+        startPagePrefix = '';
+    }
+    else if (uriPrefix.toLowerCase().substring(0, 7) === 'ms-appx') {
+        uriPrefix += '/'; // add a 3rd trailing forward slash for correct area resolution
+    }
+
+    appNode.attrib.StartPage = uriPrefix + startPagePrefix + startPage;
+}
+
 // Adjust version number as per CB-5337 Windows8 build fails due to invalid app version
 function fixConfigVersion (version) {
     if(version && version.match(/\.\d/g)) {
@@ -211,29 +280,105 @@ function fixConfigVersion (version) {
     return version;
 }
 
-function applyAccessRules (config, manifest) {
+function applyAccessRules (config, manifest, isTargetingWin10) {
     // Updates WhiteListing rules
     //<ApplicationContentUriRules>
     //    <Rule Match="https://www.example.com" Type="include"/>
     //</ApplicationContentUriRules>
+
+    var AppContentUriRulesElementName = 'ApplicationContentUriRules',
+        RuleElementName = 'Rule';
+
+    if (isTargetingWin10) {
+        return applyNavigationWhitelist(config, manifest);
+    }
+
+    
+    var accessRules = config.getAccessRules().filter(function(rule) {
+        // https:// rules are always good, * rules are always good
+        if (rule.indexOf('https://') === 0 || rule === '*') {
+            return true;
+        } else {
+            console.warn('Access rules must begin with "https://", the following rule will be ignored: ' + rule);
+        }
+        return false;
+    });
+
+    // If * is specified, emit no access rules.
+    if (accessRules.indexOf('*') > -1) {
+        accessRules = [];
+    }
+
+    createApplicationContentUriRules(manifest, AppContentUriRulesElementName, RuleElementName, accessRules, { Type: 'include' });
+}
+
+/** 
+ * Windows 10-based whitelist-plugin-compatible support for the enhanced navigation whitelist.
+ * Allows WinRT access to origins specified by <allow-navigation href="origin" /> elements.
+ */
+function applyNavigationWhitelist(config, manifest) {
+    var AppContentUriRulesElementName = 'uap:ApplicationContentUriRules';
+    var RuleElementName = 'uap:Rule';
+    var UriSchemeTest = /^(?:https?|ms-appx-web):\/\//i;
+
+    var whitelistRules = config.getNavigationWhitelistRules().filter(function(rule) {
+        if (UriSchemeTest.test(rule)) {
+            return true;
+        } else {
+            console.warn('The following navigation rule had an invalid URI scheme and is ignored: "' + rule + '".');
+        }
+        return false;
+    });
+
+    var defaultPrefix = config.getPreference('WindowsDefaultUriPrefix');
+    if ('ms-appx://' !== defaultPrefix) {
+        var hasMsAppxWeb = whitelistRules.some(function(rule) {
+            return /^ms-appx-web:\/\/\/$/i.test(rule);
+        });
+        if (!hasMsAppxWeb) {
+            whitelistRules.push('ms-appx-web:///');
+        }
+    }
+
+    createApplicationContentUriRules(manifest, AppContentUriRulesElementName, RuleElementName, whitelistRules, { 
+        Type: 'include', 
+        WindowsRuntimeAccess: 'all'
+    });
+}
+
+/**
+ * Private function used by applyNavigationWhitelist and applyAccessRules
+ * which creates the corresponding section in the app manifest.
+ * @param manifest {et.ElementTree} The manifest document.
+ * @param acurElementName {string} The name of the AccessContentUriRules element, including prefix if applicable.
+ * @param ruleElementName {string} The name of the Rule element, including prefix if applicable.
+ * @param rulesOrigins {string[]} The origins that will be permitted.
+ * @param commonAttributes {Object} Property bag of additional attributes that should be applied to every rule.
+ */
+function createApplicationContentUriRules(manifest, acurElementName, ruleElementName, rulesOrigins, commonAttributes) {
     var appUriRulesRoot = manifest.find('.//Application'),
-        appUriRules = appUriRulesRoot.find('.//ApplicationContentUriRules');
+        appUriRules = appUriRulesRoot.find(acurElementName);
 
     if (appUriRules !== null) {
         appUriRulesRoot.remove(null, appUriRules);
     }
-    // rules are not defined or allow any
-    if (accessRules.length === 0 || accessRules.indexOf('*') > -1) {
+
+    // No rules defined
+    if (rulesOrigins.length === 0) {
         return;
-    } 
+    }
 
-    appUriRules = et.Element('ApplicationContentUriRules');
+    appUriRules = et.Element(acurElementName);
     appUriRulesRoot.append(appUriRules);
 
-    accessRules.forEach(function(rule) {
-        var el = et.Element('Rule');
+    rulesOrigins.forEach(function(rule) {
+        var el = et.Element(ruleElementName);
         el.attrib.Match = rule;
-        el.attrib.Type = 'include';
+
+        var attributes = Object.keys(commonAttributes);
+        attributes.forEach(function(attributeName) {
+            el.attrib[attributeName] = commonAttributes[attributeName];
+        });
         appUriRules.append(el);
     });
 }
@@ -262,6 +407,20 @@ function sortCapabilities(manifest) {
     });
 }
 
+function checkForRestrictedCapabilities(config, manifest) {
+    var hasRemoteUris = checkForRemoteModeUris(config);
+    if (hasRemoteUris) {
+        var capabilitiesRoot = manifest.find('.//Capabilities');
+        var badCaps = checkForRestrictedRemoteCapabilityDeclarations(capabilitiesRoot);
+        if (badCaps) {
+            console.warn('The following Capabilities were declared and are restricted:');
+            console.warn('   ' + badCaps.join(','));
+            console.warn('You will be unable to on-board your app to the public Windows Store with');
+            console.warn(' these capabilities and access rules permitting access to remote URIs.');
+        }
+    }
+}
+
 function copyImages(config) {
     var platformRoot = ROOT;
     // TODO find the way to detect whether command was triggered by CLI or not
@@ -310,9 +469,11 @@ function copyImages(config) {
         {dest: 'SplashScreen.scale-100.png', width: 620, height: 300},
         // scaled images are specified here for backward compatibility only so we can find them by size
         {dest: 'StoreLogo.scale-240.png', width: 120, height: 120},
+        {dest: 'Square44x44Logo.scale-100.png', width: 44, height: 44},
         {dest: 'Square44x44Logo.scale-240.png', width: 106, height: 106},
-        {dest: 'Square71x71Logo.scale-240.png', width: 170, height: 170},
         {dest: 'Square70x70Logo.scale-100.png', width: 70, height: 70},
+        {dest: 'Square71x71Logo.scale-100.png', width: 71, height: 71},
+        {dest: 'Square71x71Logo.scale-240.png', width: 170, height: 170},
         {dest: 'Square150x150Logo.scale-240.png', width: 360, height: 360},
         {dest: 'Square310x310Logo.scale-100.png', width: 310, height: 310},
         {dest: 'Wide310x150Logo.scale-100.png', width: 310, height: 150},
@@ -386,3 +547,176 @@ function applyBackgroundColor (config, manifest, xmlnsPrefix) {
         visualElems.attrib.BackgroundColor = refineColor(bgColor);
     }
 }
+
+function applyUAPVersionToProject(projectFilePath, uapVersionInfo) {
+    var fileContents = fs.readFileSync(projectFilePath).toString().trim();
+    var xml = et.parse(fileContents);
+    var tpv = xml.find('./PropertyGroup/TargetPlatformVersion');
+    var tpmv = xml.find('./PropertyGroup/TargetPlatformMinVersion');
+
+    tpv.text = uapVersionInfo.targetUAPVersion.toString();
+    tpmv.text = uapVersionInfo.minUAPVersion.toString();
+
+    fs.writeFileSync(projectFilePath, xml.write({ indent: 4 }), {});
+}
+
+function applyTargetPlatformVersion(config, manifest, uapVersionInfo) {
+    var dependencies = manifest.find('./Dependencies');
+    while (dependencies.len() > 0) {
+        dependencies.delItem(0);
+    }
+
+    var uapVersionSet = getAllMinMaxUAPVersions(config);
+    var platformNames = Object.keys(uapVersionSet);
+    for (var i = 0; i < platformNames.length; i++) {
+        var curTargetPlatformName = platformNames[i];
+        var curTargetPlatformInfo = uapVersionSet[curTargetPlatformName];
+
+        var elem = subElement(dependencies, 'TargetDeviceFamily');
+        elem.set('Name', curTargetPlatformName);
+        elem.set('MinVersion', curTargetPlatformInfo.MinVersion.toString());
+        elem.set('MaxVersionTested', curTargetPlatformInfo.MaxVersionTested.toString());
+    }
+}
+
+// returns {minUAPVersion: Version, targetUAPVersion: Version} | false
+function getUAPVersions(config) {
+    // @param config: ConfigParser
+    var baselineVersions = MSBuildTools.getAvailableUAPVersions();
+    if (!baselineVersions || baselineVersions.length === 0) {
+        return false;
+    }
+
+    baselineVersions.sort(Version.comparer);
+
+    return {
+        minUAPVersion: baselineVersions[0],
+        targetUAPVersion: baselineVersions[baselineVersions.length - 1]
+    };
+}
+
+/** 
+ * Gets min/max UAP versions from the configuration.  If no version preferences are
+ * in the configuration file, this will provide Windows.Universal at BASE_UAP_VERSION for both min and max.
+ * This will always return a rational object or will fail; for example, if a platform expects
+ * a higher min-version than max-version, it will raise the max version to the min version.
+ * 
+ * @param config {ConfigParser} The configuration parser
+ * @return An object in the shape of: { 'Windows.Mobile': {'MinVersion': Version, 'MaxVersion': Version } } (where Version is a Version object)
+ * @exception {RangeError} Thrown if a Version string is badly formed.
+ */
+function getAllMinMaxUAPVersions(config) {
+    var uapVersionPreferenceTest = /(Microsoft.+?|Windows.+?)\-(MinVersion|MaxVersionTested)/i;
+    var platformBag = Object.create(null);
+    var preferenceList = config.getMatchingPreferences(uapVersionPreferenceTest);
+    preferenceList.forEach(function(verPref) {
+        var matches = uapVersionPreferenceTest.exec(verPref.name);
+        // 'matches' should look like: ['Windows.Universal-MinVersion', 'Windows.Universal', 'MinVersion']
+        var platformName = matches[1];
+        var versionPropertyName = matches[2];
+
+        var platformVersionSet = platformBag[platformName];
+        if (typeof platformVersionSet === 'undefined') {
+            platformVersionSet = { };
+            platformBag[platformName] = platformVersionSet;
+        }
+
+        var versionTest = Version.tryParse(verPref.value);
+        if (!versionTest) {
+            throw new RangeError('Could not comprehend a valid version from the string "' + verPref.value + '" of platform-boundary "' + verPref.name + '".');
+        }
+
+        platformVersionSet[versionPropertyName] = versionTest;
+    });
+
+    for (var platformName in platformBag) {
+        // Go through each and make sure there are min/max set
+        var versionPref = platformBag[platformName];
+        if (!versionPref.MaxVersionTested && !!versionPref.MinVersion) { // min is set, but max is not
+            versionPref.MaxVersionTested = versionPref.MinVersion;
+        }
+        else if (!versionPref.MinVersion && !!versionPref.MaxVersionTested) { // max is set, min is not
+            versionPref.MinVersion = versionPref.MaxVersionTested;
+        }
+        else if (!versionPref.MinVersion && !versionPref.MaxVersionTested) { // neither are set
+            versionPref.MinVersion = BASE_UAP_VERSION;
+            versionPref.MaxVersionTested = BASE_UAP_VERSION;
+        } 
+        else { // both are set
+            if (versionPref.MinVersion.gt(versionPref.MaxVersionTested)) {
+                versionPref.MaxVersionTested = versionPref.MinVersion;
+            }
+        }
+    }
+
+    if (Object.keys(platformBag).length === 0) {
+        platformBag['Windows.Universal'] = { MinVersion: BASE_UAP_VERSION, MaxVersionTested: BASE_UAP_VERSION };
+    }
+
+    return platformBag;
+}
+
+/**
+ * Checks to see whether access rules or 
+ * @param config {ConfigParser} The configuration parser
+ * @return {boolean} True if the config specifies remote URIs for access or start; false otherwise.
+ */
+function checkForRemoteModeUris(config) {
+    var accessRules = config.getNavigationWhitelistRules();
+    var startPage = config.startPage();
+    var test = /(https?|ms-appx-web):\/\//i;
+
+    var hasRemoteUri = test.test(startPage);
+    hasRemoteUri = hasRemoteUri || accessRules.some(function(rule) {
+        return test.test(rule);
+    });
+
+    return hasRemoteUri;
+}
+
+/** 
+ * Checks for capabilities which are Restricted in Windows 10 UAP.
+ * @param appxManifestCapabilitiesElement {ElementTree.Element} The appx manifest element for <capabilities>
+ * @return {string[]|false} An array of restricted capability names, or false.
+ */
+function checkForRestrictedRemoteCapabilityDeclarations(appxManifestCapabilitiesElement) {
+    if (!appxManifestCapabilitiesElement)
+        return false;
+
+    var hasRestrictedCapabilities = false;
+    var foundRestrictedCapabilities = [];
+
+    var children = appxManifestCapabilitiesElement.getchildren();
+    var declaredCapabilities = children.map(function(el) {
+        return el.attrib.Name;
+    });
+
+    UAP_RESTRICTED_CAPS.forEach(function(cap) {
+        if (declaredCapabilities.indexOf(cap) > -1) {
+            hasRestrictedCapabilities = true;
+            foundRestrictedCapabilities.push(cap);
+        }
+    });
+
+    return hasRestrictedCapabilities ? foundRestrictedCapabilities : hasRestrictedCapabilities;
+}
+
+/**
+ * Checks for capabilities which require the uap: prefix in Windows 10.
+ * @param appxManifestCapabilitiesElement {ElementTree.Element} The appx manifest element for <capabilities>
+ */
+function ensureUapPrefixedCapabilities(appxManifestCapabilitiesElement) {
+    var children = appxManifestCapabilitiesElement.getchildren();
+    var declaredCapabilities = children.map(function(el) {
+        return { name: el.attrib.Name, element: el, elementName: el.tag };
+    });
+
+    declaredCapabilities.forEach(function(cap) {
+        if (CAPS_NEEDING_UAPNS.indexOf(cap.name) > -1) {
+            if (cap.elementName.indexOf('uap:') === -1) {
+                cap.elementName = 'uap:' + cap.elementName;
+                cap.element.tag = cap.elementName;
+            }
+        }
+    });
+}

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/0d2387d9/template/cordova/lib/run.js
----------------------------------------------------------------------
diff --git a/template/cordova/lib/run.js b/template/cordova/lib/run.js
index 3195793..d54823d 100644
--- a/template/cordova/lib/run.js
+++ b/template/cordova/lib/run.js
@@ -32,10 +32,10 @@ module.exports.run = function (argv) {
         return Q.reject('Could not find project at ' + ROOT);
     }
 
-    // parse args
+    // parse arg
     var args  = nopt({'debug': Boolean, 'release': Boolean, 'nobuild': Boolean,
         'device': Boolean, 'emulator': Boolean, 'target': String, 'archs': String,
-        'phone': Boolean, 'win': Boolean}, {'r' : '--release'}, argv);
+        'phone': Boolean, 'win': Boolean, 'appx': String}, {'r' : '--release'}, argv);
 
     // Validate args
     if (args.debug && args.release) {
@@ -52,11 +52,43 @@ module.exports.run = function (argv) {
     var buildType    = args.release ? 'release' : 'debug',
         buildArchs   = args.archs ? args.archs.split(' ') : ['anycpu'],
         projectType  = args.phone ? 'phone' : 'windows',
-        deployTarget = args.target ? args.target : args.device ? 'device' : 'emulator';
+        deployTarget = args.target ? args.target : (args.emulator ? 'emulator' : 'device'),
+        projOverride = args.appx;
 
     // for win switch we should correctly handle 8.0 and 8.1 version as per configuration
-    if (projectType == 'windows' && getWindowsTargetVersion() == '8.0') {
-        projectType = 'windows80';
+    // as well as 10
+    var targetWindowsVersion = getWindowsTargetVersion();
+    if (projectType == 'windows') {
+        if (targetWindowsVersion == '8.0') {
+            projectType = 'windows80';
+        }
+        else if (targetWindowsVersion === '10.0') {
+            projectType = 'windows10';
+        }
+    }
+    else if (projectType === 'phone') {
+        if (targetWindowsVersion === '10.0' || targetWindowsVersion === 'UAP') {
+            projectType = 'windows10';
+        }
+    }
+
+    if (projOverride) {
+        switch (projOverride) {
+            case '8.1-phone':
+                projectType = 'phone';
+                targetWindowsVersion = '8.1';
+                break;
+            case '8.1-win':
+                projectType = 'windows';
+                targetWindowsVersion = '8.1';
+                break;
+            case 'uap':
+                projectType = 'windows10';
+                break;
+            default:
+                console.warn('Unrecognized --appx parameter passed to run: "' + projOverride + '", ignoring.');
+                break;
+        }
     }
 
     // if --nobuild isn't specified then build app first
@@ -65,10 +97,35 @@ module.exports.run = function (argv) {
     return buildPackages.then(function () {
         return packages.getPackage(projectType, buildType, buildArchs[0]);
     }).then(function(pkg) {
-        console.log('\nDeploying ' + pkg.type + ' package to ' + deployTarget + ':\n' + pkg.file);
-        return pkg.type == 'phone' ?
-            packages.deployToPhone(pkg.file, deployTarget) :
-            packages.deployToDesktop(pkg.file, deployTarget);
+        console.log('\nDeploying ' + pkg.type + ' package to ' + deployTarget + ':\n' + pkg.appx);
+        switch (pkg.type) {
+            case 'phone':
+                return packages.deployToPhoneAndRun(pkg, deployTarget, false).catch(function(e) {
+                    if (args.target || args.emulator || args.device) {
+                        throw e; // Explicit target, carry on
+                    }
+                    
+                    // 'device' was inferred initially, because no target was specified
+                    return packages.deployToPhoneAndRun(pkg, 'emulator', false);
+                });
+                
+            case 'windows10':
+                if (args.phone) {
+                    // Win10 emulator launch is not currently supported, always force device
+                    if (args.emulator || args.target === 'emulator') {
+                        console.warn('Windows 10 Phone emulator is not currently supported, attempting deploy to device.');
+                    }
+                    return packages.deployToPhoneAndRun(pkg, 'device', true);
+                }
+                else {
+                    return packages.deployToDesktop(pkg, deployTarget, projectType);
+                }
+            
+                break;    
+            default: // 'windows'
+                return packages.deployToDesktop(pkg, deployTarget, projectType);
+                
+        }
     });
 };
 
@@ -84,6 +141,9 @@ module.exports.help = function () {
     console.log('    --archs       : Specific chip architectures (`anycpu`, `arm`, `x86`, `x64`).');
     console.log('    --phone, --win');
     console.log('                  : Specifies project type to deploy');
+    console.log('    --appx=<8.1-win|8.1-phone|uap>');
+    console.log('                  : Overrides WindowsTargetVersion to build Windows 8.1, ');
+    console.log('                              Windows Phone 8.1, or Windows 10.');
     console.log('');
     console.log('Examples:');
     console.log('    run');
@@ -92,21 +152,26 @@ module.exports.help = function () {
     console.log('    run --target=7988B8C3-3ADE-488d-BA3E-D052AC9DC710');
     console.log('    run --device --release');
     console.log('    run --emulator --debug');
+    console.log('    run --device --appx=phone-8.1');
     console.log('');
+
     process.exit(0);
 };
 
 
 function getWindowsTargetVersion() {
     var config = new ConfigParser(path.join(ROOT, 'config.xml'));
-    var windowsTargetVersion = config.getPreference('windows-target-version');
+    var windowsTargetVersion = config.getWindowsTargetVersion();
     switch(windowsTargetVersion) {
-    case '8':
-    case '8.0':
-        return '8.0';
-    case '8.1':
-        return '8.1';
-    default:
-        throw new Error('Unsupported windows-target-version value: ' + windowsTargetVersion);
+        case '8':
+        case '8.0':
+            return '8.0';
+        case '8.1':
+            return '8.1';
+        case '10.0':
+        case 'UAP':
+            return '10.0';
+        default:
+            throw new Error('Unsupported WindowsTargetVersion value: ' + windowsTargetVersion);
     }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/0d2387d9/template/cordova/lib/utils.js
----------------------------------------------------------------------
diff --git a/template/cordova/lib/utils.js b/template/cordova/lib/utils.js
index 22faf13..7e7adf9 100644
--- a/template/cordova/lib/utils.js
+++ b/template/cordova/lib/utils.js
@@ -64,14 +64,27 @@ module.exports.getAppStoreUtils = function () {
     });
 };
 
+function getProgramFiles32Folder() {
+    /* jshint ignore:start */ /* Wants to use dot syntax for ProgramFiles, leaving as-is for consistency */
+    return process.env['ProgramFiles(x86)'] || process.env['ProgramFiles'];
+    /* jshint ignore:end */
+}
+
 // returns path to AppDeploy util from Windows Phone 8.1 SDK
-module.exports.getAppDeployUtils = function () {
-    var appDeployUtils = path.join((process.env['ProgramFiles(x86)'] || process.env['ProgramFiles']),
-        'Microsoft SDKs', 'Windows Phone', 'v8.1', 'Tools', 'AppDeploy', 'AppDeployCmd.exe');
-    // Check if AppDeployCmd exists
+module.exports.getAppDeployUtils = function (targetWin10) {
+    var appDeployUtils,
+        appDeployCmdName;
+    if (targetWin10) {
+        appDeployCmdName = 'WinAppDeployCmd.exe';
+        appDeployUtils = path.join(getProgramFiles32Folder(), 'Windows Kits', '10', 'bin', 'x86', appDeployCmdName);
+    } else {
+        appDeployCmdName = 'AppDeployCmd.exe';
+        appDeployUtils = path.join(getProgramFiles32Folder(), 'Microsoft SDKs', 'Windows Phone', 'v8.1', 'Tools', 'AppDeploy', appDeployCmdName);
+    }
+    // Check if AppDeployCmd is exists
     if (!fs.existsSync(appDeployUtils)) {
-        console.warn('WARNING: AppDeploy tool (AppDeployCmd.exe) wasn\'t found. Make sure that it\'s in %PATH%');
-        return Q.resolve('AppDeployCmd');
+        console.warn('WARNING: AppDeploy tool (' + appDeployCmdName + ') wasn\'t found. Make sure that it\'s in %PATH%');
+        return Q.resolve(appDeployCmdName);
     }
     return Q.resolve(appDeployUtils);
 };

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/0d2387d9/template/images/Square44x44Logo.scale-100.png
----------------------------------------------------------------------
diff --git a/template/images/Square44x44Logo.scale-100.png b/template/images/Square44x44Logo.scale-100.png
new file mode 100644
index 0000000..ce84d15
Binary files /dev/null and b/template/images/Square44x44Logo.scale-100.png differ

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/0d2387d9/template/images/Square71x71Logo.scale-100.png
----------------------------------------------------------------------
diff --git a/template/images/Square71x71Logo.scale-100.png b/template/images/Square71x71Logo.scale-100.png
new file mode 100644
index 0000000..9e712ab
Binary files /dev/null and b/template/images/Square71x71Logo.scale-100.png differ

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/0d2387d9/template/package.windows10.appxmanifest
----------------------------------------------------------------------
diff --git a/template/package.windows10.appxmanifest b/template/package.windows10.appxmanifest
new file mode 100644
index 0000000..e59e019
--- /dev/null
+++ b/template/package.windows10.appxmanifest
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+       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.
+-->
+<Package
+  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
+  xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
+  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
+  IgnorableNamespaces="uap mp">
+
+  <Identity
+    Name="$guid1$"
+    Version="1.0.0.0"
+    Publisher="CN=$username$" />
+
+  <mp:PhoneIdentity PhoneProductId="$guid1$" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
+
+  <Properties>
+    <DisplayName>$projectname$</DisplayName>
+    <PublisherDisplayName>$username$</PublisherDisplayName>
+    <Logo>images\StoreLogo.png</Logo>
+  </Properties>
+
+  <Dependencies>
+    <TargetDeviceFamily Name="Windows.Universal" MinVersion="0.0.0.0" MaxVersionTested="10.0.0.0" />
+  </Dependencies>
+
+  <Resources>
+    <Resource Language="x-generate" />
+  </Resources>
+
+  <Applications>
+    <Application
+      Id="$safeprojectname$"
+      StartPage="www/index.html">
+
+      <uap:VisualElements
+        DisplayName="$projectname$"
+        Description="CordovaApp"
+        BackgroundColor="#464646"
+        Square150x150Logo="images\Square150x150Logo.png"
+        Square44x44Logo="images\Square44x44Logo.png">
+
+        <uap:SplashScreen Image="images\splashscreen.png" />
+        <uap:DefaultTile ShortName="$projectname$" 
+                         Square310x310Logo="images\Square310x310Logo.png"
+                         Square71x71Logo="images\Square71x71Logo.png" 
+                         Wide310x150Logo="images\Wide310x150Logo.png" />
+
+      </uap:VisualElements>
+    </Application>
+  </Applications>
+
+  <Capabilities>
+    <Capability Name="internetClient" />
+  </Capabilities>
+
+</Package>

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/0d2387d9/template/www/cordova.js
----------------------------------------------------------------------
diff --git a/template/www/cordova.js b/template/www/cordova.js
index 79a6ba5..56dedcc 100644
--- a/template/www/cordova.js
+++ b/template/www/cordova.js
@@ -1289,7 +1289,10 @@ module.exports = {
         if (!window.WinJS) {
             var scriptElem = document.createElement("script");
 
-            if (navigator.appVersion.indexOf("Windows Phone 8.1;") !== -1) {
+            if (navigator.appVersion.indexOf('MSAppHost/3.0') !== -1) {
+                // Windows 10 UAP
+                scriptElem.src = '/WinJS/js/WinJS.js';
+            } else if (navigator.appVersion.indexOf("Windows Phone 8.1;") !== -1) {
                 // windows phone 8.1 + Mobile IE 11
                 scriptElem.src = "//Microsoft.Phone.WinJS.2.1/js/base.js";
             } else if (navigator.appVersion.indexOf("MSAppHost/2.0;") !== -1) {

http://git-wip-us.apache.org/repos/asf/cordova-windows/blob/0d2387d9/template/www/js/index.js
----------------------------------------------------------------------
diff --git a/template/www/js/index.js b/template/www/js/index.js
index 31d9064..87b5660 100644
--- a/template/www/js/index.js
+++ b/template/www/js/index.js
@@ -31,7 +31,7 @@ var app = {
     // deviceready Event Handler
     //
     // The scope of 'this' is the event. In order to call the 'receivedEvent'
-    // function, we must explicity call 'app.receivedEvent(...);'
+    // function, we must explicitly call 'app.receivedEvent(...);'
     onDeviceReady: function() {
         app.receivedEvent('deviceready');
     },


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org