diff --git a/assets/defaultImages/windows/1152x1920.png b/assets/defaultImages/windows/1152x1920.png index abf283d..bf0769b 100644 Binary files a/assets/defaultImages/windows/1152x1920.png and b/assets/defaultImages/windows/1152x1920.png differ diff --git a/assets/defaultImages/windows/44x44.png b/assets/defaultImages/windows/44x44.png new file mode 100644 index 0000000..71e8bcc Binary files /dev/null and b/assets/defaultImages/windows/44x44.png differ diff --git a/assets/defaultImages/windows/71x71.png b/assets/defaultImages/windows/71x71.png new file mode 100644 index 0000000..c5b2fd7 Binary files /dev/null and b/assets/defaultImages/windows/71x71.png differ diff --git a/assets/defaultImages/wp8/480x800.png b/assets/defaultImages/wp8/480x800.png index bd39266..8a8b8bb 100644 Binary files a/assets/defaultImages/wp8/480x800.png and b/assets/defaultImages/wp8/480x800.png differ diff --git a/assets/windows/wrapper.css b/assets/windows/wrapper.css new file mode 100644 index 0000000..1c534f1 --- /dev/null +++ b/assets/windows/wrapper.css @@ -0,0 +1 @@ +body { width: 100%; height: 100%; position: absolute; top: 0; left: 0; margin: 0; } .loading-progress { position: absolute; top: 50%; left: 45%; width: 10%; } .extendedSplashScreen .loading-progress { position: absolute; top: 50%; left: 20%; width: 60%; margin: 0; } .extendedSplashScreen { display: block; background-color: #000000; height: 100%; width: 100%; position: absolute; top: 0px; left: 0px; text-align: center; z-index: 10111; } .extendedSplashScreen .extendedSplashImage { position: absolute; } \ No newline at end of file diff --git a/assets/windows/wrapper.html b/assets/windows/wrapper.html new file mode 100644 index 0000000..c9cb6a5 --- /dev/null +++ b/assets/windows/wrapper.html @@ -0,0 +1,19 @@ + + + + + + + + Hello World + + +
+ Launching... + +
+ + + + + diff --git a/assets/windows/wrapper.js b/assets/windows/wrapper.js new file mode 100644 index 0000000..6399e53 --- /dev/null +++ b/assets/windows/wrapper.js @@ -0,0 +1,70 @@ +var setupExtendedSplashScreen, updateSplashScreenPositioning, + splashScreen, splashScreenEl, splashScreenImageEl, + isWindows = navigator.appVersion.indexOf("Windows Phone 8.1") === -1; + +WinJS.Application.addEventListener("activated", function (e) { + if (e.detail.kind === Windows.ApplicationModel.Activation.ActivationKind.launch) { + splashScreen = e.detail.splashScreen; + + // Listen for window resize events to reposition the extended splash screen image accordingly. + // This is important to ensure that the extended splash screen is formatted properly in response to snapping, unsnapping, rotation, etc... + window.addEventListener("resize", updateSplashPositioning, false); + + var previousExecutionState = e.detail.previousExecutionState; + var state = Windows.ApplicationModel.Activation.ApplicationExecutionState; + if (previousExecutionState === state.notRunning + || previousExecutionState === state.terminated + || previousExecutionState === state.closedByUser) { + setupExtendedSplashScreen(); + } + } +}, false); + +setupExtendedSplashScreen = function () { + splashScreenEl = document.getElementById("extendedSplashScreen"); + splashScreenImageEl = (splashScreenEl && splashScreenEl.querySelector(".extendedSplashImage")); + splashLoadingEl = (splashScreenEl && splashScreenEl.querySelector(".loading-progress")); + + if (!splashScreen || !splashScreenEl || !splashScreenImageEl) { return; } + + var imgSrc = "/images/splashScreenPhone.png" + if (isWindows) { + imgSrc = "/images/SplashScreen.png" + } + + splashScreenImageEl.setAttribute("src", imgSrc); + + updateSplashPositioning(); + + // Once the extended splash screen is setup, apply the CSS style that will make the extended splash screen visible. + splashScreenEl.style.display = "block"; +}; + +updateSplashPositioning = function () { + if (!splashScreen || !splashScreenImageEl) { return; } + // Position the extended splash screen image in the same location as the system splash screen image. + if (isWindows) { + splashScreenImageEl.style.top = splashScreen.imageLocation.y + "px"; + splashScreenImageEl.style.left = splashScreen.imageLocation.x + "px"; + splashScreenImageEl.style.height = splashScreen.imageLocation.height + "px"; + splashScreenImageEl.style.width = splashScreen.imageLocation.width + "px"; + } else { + var curOrientation = Windows.Devices.Sensors.SimpleOrientationSensor.getDefault().getCurrentOrientation(); + if ((curOrientation == Windows.Devices.Sensors.SimpleOrientation.rotated270DegreesCounterclockwise || curOrientation == Windows.Devices.Sensors.SimpleOrientation.rotated90DegreesCounterclockwise) && + Windows.Graphics.Display.DisplayInformation.autoRotationPreferences != Windows.Graphics.Display.DisplayOrientations.portrait) { + splashScreenImageEl.src = "/images/splashscreen.png"; + } else { + splashScreenImageEl.src = "/images/splashScreenPhone.png"; + } + splashScreenImageEl.style.width = "100%"; + splashScreenImageEl.style.height = "100%"; + } + + if (splashLoadingEl) { + if (isWindows) { + splashLoadingEl.style.top = (splashScreen.imageLocation.y + splashScreen.imageLocation.height + 20) + "px"; + } else { + splashLoadingEl.style.top = (window.innerHeight * 0.8) + "px"; + } + } +}; diff --git a/package.json b/package.json index 0ff9550..df0e32e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cordova-plugin-hostedwebapp", - "version": "0.1.0", + "version": "0.1.1", "description": "Hosted Web App Plugin", "cordova": { "id": "cordova-plugin-hostedwebapp", diff --git a/plugin.xml b/plugin.xml index 6276239..623f83f 100644 --- a/plugin.xml +++ b/plugin.xml @@ -1,7 +1,7 @@ + version="0.1.1"> HostedWebApp Hosted Web App Plugin MIT License @@ -11,15 +11,22 @@ - - + + - - + + + + + + + + + diff --git a/readme.md b/readme.md index 71a566a..832ca4b 100644 --- a/readme.md +++ b/readme.md @@ -205,3 +205,6 @@ Cordova for Android and iOS platforms provide a security policy to control which The Windows and Windows Phone platforms do not provide control for these kind of requests, and they will be allowed. +## Changelog + +Releases are documented in [GitHub](https://github.com/manifoldjs/ManifoldCordova/releases). \ No newline at end of file diff --git a/scripts/replaceWindowsWrapperFiles.js b/scripts/replaceWindowsWrapperFiles.js new file mode 100644 index 0000000..042a1d7 --- /dev/null +++ b/scripts/replaceWindowsWrapperFiles.js @@ -0,0 +1,140 @@ +#!/usr/bin/env node + +var fs = require('fs'), + path = require('path'), + url = require('url'), + etree, + projectRoot; + +var logger = { + log: function () { + if (process.env.NODE_ENV !== 'test') { + console.log.apply(this, arguments) + } + }, + warn: function() { + if (process.env.NODE_ENV !== 'test') { + console.warn.apply(this, arguments) + } + } +}; + +function copyFile(source, target, callback) { + var cbCalled = false; + + function done(err) { + if (!cbCalled) { + callback(err); + cbCalled = true; + } + } + + var rd = fs.createReadStream(source); + rd.on('error', done); + + var wr = fs.createWriteStream(target); + wr.on('error', done); + wr.on('close', function() { + done(); + }); + rd.pipe(wr); +}; + +function updateManifestFile(manifestPath) { + var contents = fs.readFileSync(manifestPath, 'utf-8'); + if(contents) { + //Windows is the BOM. Skip the Byte Order Mark. + contents = contents.substring(contents.indexOf('<')); + } + + var startPage = "www/wrapper.html"; + var manifest = new etree.ElementTree(etree.XML(contents)); + var appNode = manifest.find('.//Application'); + + appNode.attrib.StartPage = startPage; + + // Write out manifest + fs.writeFileSync(manifestPath, manifest.write({indent: 4}), 'utf-8'); +} + +function updateWindowsManifests() { + var MANIFEST_WINDOWS8 = 'package.windows80.appxmanifest', + MANIFEST_WINDOWS = 'package.windows.appxmanifest', + MANIFEST_PHONE = 'package.phone.appxmanifest', + MANIFEST_WINDOWS10 = 'package.windows10.appxmanifest'; + + // Apply appxmanifest changes + [ MANIFEST_WINDOWS, + MANIFEST_WINDOWS8, + MANIFEST_PHONE, + MANIFEST_WINDOWS10 ].forEach( + function(manifestFile) { + updateManifestFile(path.join(projectRoot, "platforms", "windows", manifestFile)); + }); +} + +module.exports = function (context) { + projectRoot = context.opts.projectRoot; + + // if the windows folder does not exist, cancell the script + var windowsPath = path.join(projectRoot, "platforms","windows"); + if (!fs.existsSync(windowsPath)) { + return; + } + + etree = context.requireCordovaModule('cordova-lib/node_modules/elementtree'); + + // move contents of the assets folder to the windows platform dir + var Q = context.requireCordovaModule('q'); + + var filename = "wrapper"; + + var sourcePath = path.resolve(__dirname, "..", "assets", "windows", "wrapper.html"); + var destPath = path.join(projectRoot, "platforms","windows", "www", filename + ".html"); + + logger.log('Copying wrapper html file for the windows platform from '+ sourcePath + ' to ' + destPath + '.'); + + var task = Q.defer(); + copyFile(sourcePath, destPath, function (err) { + if (err) { + console.error(err); + return task.reject(err); + } + + console.log("Finished copying wrapper html file for the windows platform."); + + var sourcePath = path.resolve(__dirname, "..", "assets", "windows", "wrapper.js"); + var destPath = path.join(projectRoot, "platforms", "windows", "www", "js", filename +".js"); + + logger.log('Copying wrapper js file for the windows platform from '+ sourcePath + ' to ' + destPath + '.'); + + copyFile(sourcePath, destPath, function (err) { + if (err) { + console.error(err); + return task.reject(err); + } + + console.log("Finished copying wrapper js file for the windows platform."); + + var sourcePath = path.resolve(__dirname, "..", "assets", "windows", "wrapper.css"); + var destPath = path.join(projectRoot, "platforms", "windows", "www", "css", filename + ".css"); + + logger.log('Copying wrapper css file for the windows platform from '+ sourcePath + ' to ' + destPath + '.'); + + copyFile(sourcePath, destPath, function (err) { + if (err) { + console.error(err); + return task.reject(err); + } + + console.log("Finished copying wrapper css file for the windows platform."); + + updateWindowsManifests(); + + task.resolve(); + }); + }); + }); + + return task.promise; +}; diff --git a/scripts/rollbackWindowsWrapperFiles.js b/scripts/rollbackWindowsWrapperFiles.js new file mode 100644 index 0000000..7e47be4 --- /dev/null +++ b/scripts/rollbackWindowsWrapperFiles.js @@ -0,0 +1,104 @@ +#!/usr/bin/env node + +var createConfigParser = require('./createConfigParser'), + fs = require('fs'), + path = require('path'), + url = require('url'), + pendingTasks = [], + Q, + config, + projectRoot, + etree; + +var logger = { + log: function () { + if (process.env.NODE_ENV !== 'test') { + console.log.apply(this, arguments) + } + }, + warn: function() { + if (process.env.NODE_ENV !== 'test') { + console.warn.apply(this, arguments) + } + } +}; + +function deleteFile(path) { + var t = Q.defer(); + pendingTasks.push(t); + + logger.log('Deleting ' + path + ' file for the windows platform.'); + + fs.unlink(path, function (err) { + if (err) { + console.log(err); + return t.reject(); + } + + t.resolve(); + }); +} + +// Configure Cordova configuration parser +function configureParser(context) { + var cordova_util = context.requireCordovaModule('cordova-lib/src/cordova/util'), + ConfigParser = context.requireCordovaModule('cordova-lib/src/configparser/ConfigParser'); + etree = context.requireCordovaModule('cordova-lib/node_modules/elementtree'); + + var xml = cordova_util.projectConfig(context.opts.projectRoot); + config = createConfigParser(xml, etree, ConfigParser); +} + +module.exports = function (context) { + // If the plugin is not being removed, cancel the script + if (context.opts.plugins.indexOf(context.opts.plugin.id) == -1) { + return; + } + + var projectRoot = context.opts.projectRoot; + + // if the windows folder does not exist, cancell the script + var windowsPath = path.join(projectRoot, "platforms","windows"); + if (!fs.existsSync(windowsPath)) { + return; + } + + Q = context.requireCordovaModule('q'); + var task = Q.defer(); + + var destPath = path.join(projectRoot, "platforms", "windows", "www", "wrapper.html"); + if (fs.existsSync(destPath)) { + deleteFile(destPath); + } + + destPath = path.join(projectRoot, "platforms", "windows", "www", "js", "wrapper.js"); + + if (fs.existsSync(destPath)) { + deleteFile(destPath); + } + + destPath = path.join(projectRoot, "platforms", "windows", "www", "css", "wrapper.css"); + + if (fs.existsSync(destPath)) { + deleteFile(destPath); + } + + Q.allSettled(pendingTasks).then(function (e) { + console.log("Finished removing assets for the windows platform."); + + // restore content source to index.html in all platforms. + configureParser(context); + if (config) { + console.log("Restoring content source value to index.html"); + config.setAttribute('content', 'src', 'index.html'); + config.write(); + } + else { + console.log("could not load config.xml file"); + } + + task.resolve(); + }); + + return task.promise; +}; diff --git a/scripts/test/updateConfiguration.js b/scripts/test/updateConfigurationBeforePrepare.js similarity index 96% rename from scripts/test/updateConfiguration.js rename to scripts/test/updateConfigurationBeforePrepare.js index 4cac3fc..db6ff93 100644 --- a/scripts/test/updateConfiguration.js +++ b/scripts/test/updateConfigurationBeforePrepare.js @@ -1,7 +1,7 @@ 'use strict'; process.env.NODE_ENV = 'test'; -var updateConfiguration = require('../updateConfiguration'); +var updateConfiguration = require('../updateConfigurationBeforePrepare'); var tu = require('./test-utils'); var assert = require('assert'); @@ -17,7 +17,7 @@ function initializeContext(testDir) { var ctx = { opts : { plugin: { - id: 'com-manifoldjs-hostedwebapp' + id: 'cordova-plugin-hostedwebapp' }, projectRoot : testDir } diff --git a/scripts/updateConfigurationAfterBuild.js b/scripts/updateConfigurationAfterBuild.js deleted file mode 100644 index eb778ff..0000000 --- a/scripts/updateConfigurationAfterBuild.js +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env node - -var createConfigParser = require('./createConfigParser'), - fs = require('fs'), - path = require('path'), - windowsConfig, - projectRoot, - etree; - -var logger = { - log: function () { - if (process.env.NODE_ENV !== 'test') { - console.log.apply(this, arguments) - } - } -}; - -// Configure Cordova configuration parser -function configureParser(context) { - var cordova_util = context.requireCordovaModule('cordova-lib/src/cordova/util'), - ConfigParser = context.requireCordovaModule('cordova-lib/src/configparser/ConfigParser'); - etree = context.requireCordovaModule('cordova-lib/node_modules/elementtree'); - - var windowsDir = path.join(projectRoot, 'platforms', 'windows'); - if (fs.existsSync(windowsDir)) { - var windowsXml = cordova_util.projectConfig(windowsDir); - windowsConfig = createConfigParser(windowsXml, etree, ConfigParser); - } -} - -module.exports = function (context) { - // create a parser for the Cordova configuration - projectRoot = context.opts.projectRoot; - configureParser(context); - - if (windowsConfig) { - logger.log('Removing default images from windows configuration...'); - windowsConfig.removeElements('.//icon[@hap-default-image=\'yes\']'); - windowsConfig.removeElements('.//splash[@hap-default-image=\'yes\']'); - - windowsConfig.write(); - } -} diff --git a/scripts/updateConfigurationAfterPrepare.js b/scripts/updateConfigurationAfterPrepare.js index b6b292a..02bc7a5 100644 --- a/scripts/updateConfigurationAfterPrepare.js +++ b/scripts/updateConfigurationAfterPrepare.js @@ -4,7 +4,9 @@ var createConfigParser = require('./createConfigParser'), fs = require('fs'), path = require('path'), config, - windowsConfig, + windowsConfig, + androidConfig, + iosConfig, projectRoot, etree; @@ -24,32 +26,64 @@ function configureParser(context) { var xml = cordova_util.projectConfig(projectRoot); config = createConfigParser(xml, etree, ConfigParser); - + var windowsDir = path.join(projectRoot, 'platforms', 'windows'); if (fs.existsSync(windowsDir)) { var windowsXml = cordova_util.projectConfig(windowsDir); windowsConfig = createConfigParser(windowsXml, etree, ConfigParser); } + + var androidDir = path.join(projectRoot, 'platforms', 'android', 'res', 'xml'); + if (fs.existsSync(androidDir)) { + var androidXml = cordova_util.projectConfig(androidDir); + androidConfig = createConfigParser(androidXml, etree, ConfigParser); + } + + var iosProjectName = config.name(); + if (iosProjectName) { + var iosDir = path.join(projectRoot, 'platforms', 'ios', iosProjectName); + if (fs.existsSync(iosDir)) { + var iosXml = cordova_util.projectConfig(iosDir); + iosConfig = createConfigParser(iosXml, etree, ConfigParser); + } + } } module.exports = function (context) { - logger.log('Removing default images from cordova configuration...'); - // create a parser for the Cordova configuration projectRoot = context.opts.projectRoot; configureParser(context); - // Remove default images from configuration file + logger.log('Removing default images from Cordova configuration...'); + + // Remove default images from root configuration file config.removeElements('.//icon[@hap-default-image=\'yes\']'); config.removeElements('.//splash[@hap-default-image=\'yes\']'); // save the updated configuration config.write(); - + if (windowsConfig) { - // Patch for windows: restoring the start page to index.html - logger.log('Restoring local start page in windows configuration...'); - windowsConfig.setAttribute('content', 'src', 'index.html'); - windowsConfig.write(); + // Remove default images from windows configuration file + windowsConfig.removeElements('.//icon[@hap-default-image=\'yes\']'); + windowsConfig.removeElements('.//splash[@hap-default-image=\'yes\']'); + + windowsConfig.write(); + } + + if (androidConfig) { + // Remove default images from android configuration file + androidConfig.removeElements('.//icon[@hap-default-image=\'yes\']'); + androidConfig.removeElements('.//splash[@hap-default-image=\'yes\']'); + + androidConfig.write(); + } + + if (iosConfig) { + // Remove default images from ios configuration file + iosConfig.removeElements('.//icon[@hap-default-image=\'yes\']'); + iosConfig.removeElements('.//splash[@hap-default-image=\'yes\']'); + + iosConfig.write(); } } diff --git a/scripts/updateConfiguration.js b/scripts/updateConfigurationBeforePrepare.js similarity index 96% rename from scripts/updateConfiguration.js rename to scripts/updateConfigurationBeforePrepare.js index 9e20cc1..a19c453 100644 --- a/scripts/updateConfiguration.js +++ b/scripts/updateConfigurationBeforePrepare.js @@ -116,7 +116,7 @@ function configureParser(context) { etree = context.requireCordovaModule('cordova-lib/node_modules/elementtree'); var xml = cordova_util.projectConfig(projectRoot); - config = createConfigParser(xml, etree, ConfigParser); + config = createConfigParser(xml, etree, ConfigParser); } function processAccessRules(manifest) { @@ -539,8 +539,10 @@ function processAndroidIcons(manifestIcons, outputConfiguration, previousIndent) function processWindowsIcons(manifestIcons) { var iconSizes = [ "30x30", + "44x44", "106x106", "70x70", + "71x71", "170x170", "150x150", "360x360", diff --git a/src/windows/HostedWebAppPluginProxy.js b/src/windows/HostedWebAppPluginProxy.js index 2640361..cf26e25 100644 --- a/src/windows/HostedWebAppPluginProxy.js +++ b/src/windows/HostedWebAppPluginProxy.js @@ -10,11 +10,6 @@ var _whiteList = []; // creates a webview to host content function configureHost(url, zOrder, display) { - - // workaround to avoid the webview scaling issue - var div = document.getElementById("deviceready"); - div.classList.remove("blink"); - var webView = document.createElement(cordova.platformId === 'windows8' ? 'iframe' : 'x-ms-webview'); var style = webView.style; style.position = 'absolute'; @@ -166,6 +161,27 @@ function configureWhiteList(manifest) { } } +// hides the extended splash screen +function hideExtendedSplashScreen(e) { + var extendedSplashScreen = document.getElementById("extendedSplashScreen"); + extendedSplashScreen.style.display = "none"; +} + +// handle the hardware backbutton +function navigateBack(e) { + if (!_mainView.canGoBack) { + return false; + } + + try { + _mainView.goBack(); + } catch (err) { + return false; + } + + return true; +} + module.exports = { // loads the W3C manifest file and parses it loadManifest: function (successCallback, errorCallback, args) { @@ -231,5 +247,8 @@ module.exports.loadManifest( configureOfflineSupport('offline.html'); configureWhiteList(manifest); _mainView = configureHost(manifest ? manifest.start_url : 'about:blank', _zIndex); + _mainView.addEventListener("MSWebViewDOMContentLoaded", hideExtendedSplashScreen, false); + cordova.fireDocumentEvent("webviewCreated", { webView: _mainView }); - }); \ No newline at end of file + WinJS.Application.onbackclick = navigateBack; + });