Le Post Infeeny

Les articles des consultants et experts Infeeny

Archives de Catégorie: JavaScript

Écriture/lecture/modification de fichiers dans un projet Universel app/UWP et cordova

Dans nos applications, nous avons pris le parti d’écrire nos fichiers en JSON. Nous avons donc conçu une abstraction (en WinJS, avec un système de promises) qui nous permet d’utiliser des méthodes claires et simples sur toutes les plateformes (WinRT/UWP ou Cordova (iOS/Android) )

Ce module « DataContainer » est disponible dans notre libraire WinJSContrib (Github)

Dans WinJSContrib.DataContainer, nous avons 4 fichiers qui exposent les mêmes méthodes avec des implémentations différentes :

  • read : lecture d’un élément
  • save : enregistrement d’un élément
  • remove : suppression d’un élément
  • list : liste des fichiers d’un container
  • child : création/accès container enfant

Dans une application WinRT/UWP nous allons naturellement utiliser la couche WinRT/UWP pour écrire/lire les fichiers. Pour cela il suffit d’inclure le fichier « winjscontrib.datacontainer.winrt.file.js »

Dans une application cordova IOS ou Android, nous avons plusieurs choix :

  • Utiliser une base de données : winjscontrib.datacontainer.cordova.database.js
  • Utiliser le système de fichiers avec le plugin file : « winjscontrib.datacontainer.cordova.file.js »
  • Utiliser le localStorage : « winjscontrib.datacontainer.localstorage.js » (utilisable en WinRT aussi)

Ensuite, il faut instancier un container parent et l’utiliser partout dans l’application.

Dans le JS des applications Windows 8/Phone il suffit d’instancier le container (dossier) parent :

MyApp.Data.container = new WinJSContrib.DataContainer.WinRTFilesContainer(undefined, { logger: WinJSContrib.Logger });

Et changer WinRTFilesContainer par notre choix (CordovaDatabaseContainer par exemple) pour l’application cordova en faisant attention de ne l’appeler qu’après l’enclenchement de l’évènement deviceready.

Et c’est tout, la magie s’opère à l’intérieur de notre librairie 🙂

Quelques exemples d’utilisation :

  • Pour lire un fichier :
  • Data.container.read("objKey").then(function (data) { }, function (error) { });
    
  • Supprimer un fichier :
  • Data.container.remove("objKey").then(function () { }, function (error) { });
    
  • Enregistrer un fichier :
  • Data.container.save("objKey",obj).then(function () { }, function (error) { });
    
  • Accès à un container enfant (sous dossier) avec la lecture d’un fichier fichier :
  • Data.container.child(folderid).read("subObjKey").then( function (subObjInFolderID) { }, function (error) { }));
    
  • Liste des fichiers dans un container :
  • Data.container.list().then(function (res) {}, function (error) { });
    

Auditing your Windows Javascript UWP application in production

Since the launch of Windows 8, I’m writing native Windows applications using HTML and JavaScript, for the Windows store and for enterprise applications. Believe it or not but I’m doing it full time and, so far, I’m really enjoying it. I know many people will disagree, especially in the Windows ecosystem, but I’m really enjoying the productivity and the expressiveness you get by writing rich client applications using HTML. And the beauty with Windows 8, and now Windows 10, is that you have full access in JavaScript to the Windows API, from rich file access, to sensors, Cortana integration, notifications, etc.

Even better, with Windows 10 and the Universal Windows Platform (or UWP) you could write one single application using HTML and JavaScript that will run on Windows desktop, laptop, tablet, phone, IoT devices, Xbox, Hololens and probably much more.

If you are familiar with the web ecosystem, you could also very easily reuse most or all of your skills and application code and use it to build awesome websites, or target other devices, with Github electron to make a Mac OsX version, or use Cordova or React native to reach iOS or Android. It really is an exciting time for web developpers.

As exciting as those experiences have been, there are a few things that are still frustrating. No matter what technology you use for development, one of them is the ability to detect and troubleshoot problems when your application have been released into the wild. Visual Studio is really great and provide many tools to help debug and audit your app on your dev box, but for troubleshooting applications in production, you’re naked in the dark.

Luckily when using web technologies, you could use some wonderful tools like Vorlon.js. This tool is really great because you could target any device, without any prerequisite. You have nothing to install on the client to have great diagnostics from your app.
I previously explained how to use Vorlon.js in production, and how to use it with your JavaScript UWP, follow those post if you want to setup the stage. In this post, we will see an overview of what you can achieve with Vorlon.js for a Windows UWP app in production, and a few guidance. What we will see is not specific to a JavaScript UWP. You could use the same things to debug a Webview in a C# or C++ application.

Inspect the DOM

Vorlon.js is designed from the ground with extensibility in mind. It features many plugins, and you could really easily write your own (we will talk more on that later). One of the most usefull for frontend applications is the DOM explorer. It’s very much like the tools you will find in the developper tools of your favorite browser.
For troubleshooting issues in production, it is very interesting to see what appened into the DOM, what styles are applyed, check the current size of the user screen, etc.

domexplorer.jpg

 

Watch the console

Another core plugin of Vorlon is the console. It will shows all console entries in your dashboard. If you put meaningful console logging into your app, this plugin is insanely usefull because you see in (almost) realtime what the user is doing, which is key to reproduce the issue. With some luck (and coding best practices), you could also have errors popping directly in the console with the stack trace (if the Promise god is with you). The console also feature an « immediate window » where you could issue simple JavaScript commands.

console.jpg

Check objects value

You could also have access to global JavaScript variables using Object explorer. This plugin allow you to watch values from your objects, and explore graph of objects. It’s always interesting to be able to keep an eye on your application state when trying to troubleshoot your application.

objexplorer

Monitoring CPU, memory, disk, …

You sometimes have to face more insidious bugs, like memory leaks, that usually requires advanced debugging tools. In the comfort of your dev box, you could use IDEs like Visual Studio to diagnose those bugs with specific tools. Modern browsers usually have similar tools, but does not have APIs to check things like memory or CPU that you could use remotely. Fortunately, when you are targeting Windows UWP, you could have access to all modern Windows APIs, and especially those about diagnostics.

That’s why the Vorlon team implemented a plugin specific to Windows UWP. It uses WinRT APIs for diagnostics and for different metadata from the client. For now, this plugin is not released, and you will have to look at the dev branch to try it out.

uwp.jpg

From the application metadata, you will get infos, such as its name, but also the application version, current language, and device family.

uwpmetadata

You will also have a glimpse at the network kind and status, and the battery level of the device.

It enables you to monitor the CPU, the memory and disk I/O. It’s especially usefull to track memory leaks, or excessive resources consumption.

uwpmemory.jpg

And much more…

You have many more plugins within Vorlon that will help you diagnose your app, monitor xhr calls, explore resources like localstorage, check for accessibility and best practices, and so on. We cannot cover them all in one post, but I hope you get a decent idea of the many possibilities it is unlocking to help you improve your applications, both during development and more than anything, in your production environment. It’s especially usefull for mobile applications such as Windows UWP.

Writing plugin for Vorlon.js is really easy. You have to implement one javascript class for the client, and one other for displaying results in the dashboard. Writing a simple plugin can sometimes helps you save a lot of time because it can helps you analyse the problem when and where they occur. If you are proud of your plugin, submit it back to Vorlon.js !

Using Vorlon.js in production

Vorlon.js is a great debugging and auditing tool. It is designed to help you with any web technology. It works great for a website, an application with Apache Cordova or with Windows UWP applications, or even now with Node.js and Office addin (as the time of this writing, Office addin support is in dev branch).

It is great during development because you have one common tool to address many different devices, but that’s not the only way to use it.

Have you ever had a mobile website that shows problems on some device that you don’t have at hand ? or users reporting strange behavior and you cannot access it ? Vorlon can really help you diagnose problems in production, having access to console, objects, and so. But in production, you might have to integrate it in a slightly different way. In production, you probably don’t want to have it active by default to save performances, battery, network and other resources.

Fortunately, Vorlon.js comes with a small helper that will help you to turn it on, on demand. The idea is to embed a very tiny client in your application, and provide a way to your user to activate Vorlon. It could be a button in your about page, an easter egg of some sort (why not using the Konami code ?), or whatever way you would like.

Adding production client library

Let’s see how to do it. If you run Vorlon locally with default options, you have it running on « http://localhost:1337 ». The client script is available at « http://localhost:1337/vorlon.production.js ». Add a script tag pointing to it in your application like this :

<script src="http://localhost:1337/vorlon.production.js"></script>

Add it first in the head of your page, and immediately after, create an instance of Vorlon.Production :

<script type="text/javascript">
if (VORLON && VORLON.Production){
    var vorlonProd = new VORLON.Production("http://localhost:1337", "mysessionid");
}
</script>

By default, this code will not do anything, and it will have no impact on your app. It just allows you to turn Vorlon on when you will need it. As you probably noticed, you specify the URI for your Vorlon server, and the session id you want to use.

Turning Vorlon.js on

Now you could use your instance of Vorlon.Production to activate it with a simple function call.

vorlonProd.activate();

The call to activate will add the vorlon client script to your page. It also adds a flag in sessionStorage. It means that Vorlon will still be active for the lifetime of your browser if your user navigate from page to page (or the lifetime of your app if you use Cordova or Windows UWP).

Sometimes you may want to force your page to reload when activating Vorlon, especially when you are auditing a single page application. To do that, just add true to your call to activate.

vorlonProd.activate(true);

Persisting Vorlon activation

Some bugs die hard. You may want to persist Vorlon activation and deactivate it explicitely. To do so, you must create your Vorlon.Production with an additional argument :

<script type="text/javascript">
if (VORLON && VORLON.Production){
    var vorlonProd = new VORLON.Production("http://localhost:1337", "mysessionid",true);
}
</script>

With that argument, activation token will persist using localStorage instead of sessionStorage. It means Vorlon will be active until you explicitely turn it off. You could do it by calling « vorlonProd.deactivate() ».

We really hope you are enjoying Vorlon.js. Feel free to get in touch through the GitHub page.

If you run Vorlon, you could try the production helper in the sample page.

Using Vorlon.js with your Windows 10 JavaScript UWP

Vorlon.js is a great tool for diagnosing and auditing any application built with web technologies, and Windows JavaScript applications, or UWP are no exception. It means you could use Vorlon to diagnose your app running on PC, tablet, phone, Xbox, Hololens, Raspberry Pi and any device supporting Windows IoT.

However, for being able to use Vorlon.js in a UWP, you will have to configure your application sandbox to enable communication between your app and Vorlon.js server (or desktop app).

In this post, we will illustrate the different aspects for a packaged application (a JavaScript application that embeds pages, scripts, styles, …) because it is the most complicated, but what we will see here will work equally well for a hosted app (an app where pages, scripts and styles are hosted on a web server). In fact, what we will see here could also help you using Vorlon.js in a webview for a C# application.

Put your application in web context

This step is very specific to packaged applications. Packaged applications runs in a very specific security context where resources can only be loaded from inside your package. It means that you cannot use a script tag which « src » attribute points to a resource outside of your package.

For using Vorlon, you will have to force your app into a web context, allowing you to use alien resources. Be aware that doing this is a weakpoint in your app’s sandbox. It’s not a major one but do it only if you have the need to.

Putting your app in web context is very easy. You just need to update a couple entries in the manifest of your application.

First, you must change your start page. If your start page is named « default.html », replace it with « ms-appx-web:///default.html ». You could do it by editing the appxmanifest.xml, or by opening your manifest within Visual Studio.

startpage

In application context, you have access to WinRT API, but not in web context. To bring it back; you must add your start page to Content URIs. Again, you could do it manually in your manifest or with Visual Studio. Go to the content URIs tab and add a URI to « ms-appx-web:///default.html ». Don’t forget to enable WinRT by choosing « All » in « WinRT Access ».

contenturi

Allow your Vorlon.js client script

Now we must allow our app to access Vorlon.js client script. This step is required for packaged and hosted JavaScript UWP, or if you want to use Vorlon in a webview in a C# app.

First you must take note of your Vorlon.js server URI, and add it to the content URIs for your app. I will use a local Vorlon instance running on localhost on port 1337.

Open your manifest with Visual Studio and go to the Content URIs tab. Add Vorlon server URI to the list. In my case, I must add « http://localhost:1337 &raquo;.

contenturi2

Enjoy !

You are ready to go, just start your Vorlon server and your app. If you have followed the steps above, you are now able to inspect your app. The screen capture bellow shows the WinRT API with object explorer !

vorlon

If it’s not working properly, look for messages in the console in Visual Studio. You probably have misspelled some URI and errors should show up there.

Happy Vorlon.js 🙂

 

How to use Vorlon.js desktop

Vorlon.js is now available as a desktop version for Windows and Mac OSX. You could dowload them on Vorlon.js website and get started using Vorlon in a few seconds.

If you want to know more about Vorlon, check out this getting started video. There are also some great articles about using Vorlon.js. In this post, we will focus on the desktop version and its UI.

Vorlon desktop helps you use Vorlon to debug your website without setup  pre-requisites, and help you start and configure your environment very easily.

screen.PNG

On the left side, you have a menu bar.

menu

It gives you access to Vorlon’s logs, settings, and the about page with usefull info and links.

The default settings will probably fit your needs, but you could change it with the dedicated screen. It might be usefull, especially if you need to change ports (if you want to run it side by side with the server version of Vorlon for example).

config

To start debugging your website using Vorlon.js, just add this script to your website or application (it’s so usefull for cordova applications !):

<script src="http://MCNTAB011:1337/vorlon.js"></script>

Vorlon has a session mecanism to differenciate client web sites and applications. If you want to define a session, you just have to specify it in the url like this :

<script src="http://MCNTAB011:1337/vorlon.js/mysession"></script>

When using the desktop application, it’s likely that you will stick to the home screen. From there, you could see if Vorlon server is running, and start or stop it easily (Vorlon server will start automatically when you launch the application).

vorlon status

The desktop version also helps you manage your sessions, and configure Vorlon plugins for each session. The home page displays the list of connected and configured sessions. You could add and configure a session even if no client is connected.

sessions.PNG

The green dot indicate that the session is in use. The empty dot means that there is no client connected.

session options.PNG

For each session, you could see the number of connected clients, and open the Dashboard for that session.

dashboard.PNG

The last button helps you configure your session. You could choose if the client must receive socket.io (if your website already use socket.io, turning this off may helps), and turn each plugin on or off.

session config.PNG

We really hope you will enjoy the desktop version. It’s a great way to get started using Vorlon.js. We welcome any feedback so feel free to join us on github to log your feedback or any issue you may have.

 

Bringing Vorlon.js to the desktop

Setting up Vorlon.js is really simple. You just have to clone the github repository and run 2 simple shell commands :
npm install
npm start
If you are familiar with git and node.js it really is a very easy and usual way to setup a web tool. If it sounds like a foreign language to you, I sincerely invite you to dig a little to become familiar with this. But…
When you look away from the blog posts writers, you will find out that many developpers don’t know about git and node.js. Some call them « dark matter developpers ». For those people, « npm install » may feel scary, and will begin by an internet search with something like « what is npm ». As a contributor to Vorlon.js I really feel concerned about user adopting and trying Vorlon for their every day « web debugging in hostile environment », not just the up-to-date developpers.
What if we could simply wrap Vorlon into an executable ? just download, and run the executable and you’re good to go. Well, we have done it, and it’s cross platform. The Windows and Mac OSX are available right now on Vorlon.js website. The linux version is missing for now. To be honest it’s just because I don’t have a linux machine at hand, and could not find time for setting up a VM. If you are volunteering for building the linux version, please get in touch !

Making a cross platform desktop app with web technologies

Vorlon.js is written with Node.js, and rewriting it with another technology would have been unwise, and a pain to maintain. In the web world you have some tools to make desktop apps. The more advanced projects are NW.js (aka node webkit) and electron (aka atom shell). Both tools works by combining Chromium (Google Chrome open source engine) and node.js. Those tools have subtle differences I won’t detail here.

For Vorlon we choose electron because it’s more oriented on the node.js part, but nw.js would probably have been great.

Making this desktop version has been easier than I previously thought, and it was really fun. The hardest part was about learning more on node.js to spawn Vorlon.js server process. The rest was just a thin ui layer on top of configuration and displaying active sessions. We intentionnally choose to keep the session dashboard as it is in the web version.

How to build the desktop version

If you want to see for yourself how easy it is, you could get to the github repository and grab the sources. Sources for the application is in the « desktop » folder. You will have to run « npm install » in the root folder to build Vorlon server. After that, you must install the modules for the desktop application. We do not initialize them with other parts of Vorlon because electron is a little fat and will take some time to download. Go to the « desktop » folder and run « npm install » and « npm start » in that folder. You should now see the desktop app running.
All the vorlon team is really proud of this new addition, and we hope it will make it easier for you to try Vorlon and debug your web sites and applications. If you have troubles or any suggestion, feel free to log it on github.

Hey Cortana, do you speak JavaScript?

In this article I will try to describe every steps to use Cortana with your JavaScript UWP app.

The first step is to create an xml file that represents the Voice Command Definition :

<?xml version="1.0" encoding="utf-8" ?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
  <CommandSet xml:lang="fr-fr" Name="VDM_fr-fr">
    <CommandPrefix> VDM, </CommandPrefix>
    <Example> Affiche les dernières VDM </Example>
    
    <Command Name="showlast">
      <Example> Affiche les dernières VDM </Example>
      <ListenFor RequireAppName="BeforeOrAfterPhrase"> Affiche [les] dernières </ListenFor>
      <ListenFor RequireAppName="BeforeOrAfterPhrase"> Affiche [mes] dernières </ListenFor>
      <ListenFor RequireAppName="BeforeOrAfterPhrase"> Ouvre [les] dernières </ListenFor>
      <ListenFor RequireAppName="BeforeOrAfterPhrase"> montre [moi] [les] dernières </ListenFor>
      <Feedback> affichage des dernières VDM </Feedback>
      <Navigate />
    </Command>
    <Command Name="showcategorie">
      <Example> Affiche les VDM de travail</Example>
      <ListenFor RequireAppName="ExplicitlySpecified"> ouvre les {builtin:AppName} [de] {cat}</ListenFor>
      <ListenFor RequireAppName="ExplicitlySpecified"> affiche les {builtin:AppName} [de] {cat}</ListenFor>
      <Feedback> ouverture des VDM de {cat}</Feedback>
      <Navigate />
    </Command>

    <PhraseList Label="cat">
    </PhraseList>
  </CommandSet>
  <CommandSet xml:lang="en-us" Name="VDM_en-us">
    <CommandPrefix> FML </CommandPrefix>
    <Example> Show me the latest </Example>

    <Command Name="showlast">
      <Example> Show me the latest </Example>
      <ListenFor RequireAppName="AfterPhrase"> show [me] the latest </ListenFor>
      <ListenFor RequireAppName="AfterPhrase"> open the latest </ListenFor>
      <ListenFor RequireAppName="AfterPhrase"> display the latest </ListenFor>
      <Feedback>  display of the last FML</Feedback>
      <Navigate />
    </Command>

    <Command Name="showcategorie">
      <Example> Displays the FML of love </Example>
      <ListenFor RequireAppName="ExplicitlySpecified"> Opens the  {builtin:AppName} [of] {cat}</ListenFor>
      <ListenFor RequireAppName="ExplicitlySpecified"> Displays the {builtin:AppName} [of] {cat}</ListenFor>
      <Feedback> opening FML of {cat}</Feedback>
      <Navigate />
    </Command>

    <PhraseList Label="cat">
    </PhraseList>
  </CommandSet>

</VoiceCommands>
  • In this file the root element is the VoiceCommands Element, it’s contains a list of commandSet elements. Each commandSet is for a language.
    • An commandSet contains a list of command (and others things …)
      • A command is a “command” and contains an example, and multiple elements of ListenFor, a feedback element, and an instruction element (navigate in the first sample) that explicitly specifies that this command will navigate to your app.
        • ListenFor is the command phrase, it has a RequireAppName attribute that specifies where the app name can appear in the voice command.
      • A PhraseList that contains multiple item, each Item specifies a word or phrase that can be recognized to initiate the command that references the PhraseList (optional). You can optionnaly load dynamically a list of items from your code.

You have to be very careful, when you write this file! If you don’t respect the structure or If you miss an element, the Cortana API will fall in error without any information*.

The final step.
Instantiate your new XML file of VCD, in your JavaScript code.

In this following code, I call initCortana function to initialize the VCD file using de VoiceCommandDefinitionManager API located in the Windows.ApplicationModel.VoiceCommands namespace.

You have to pass your xml file to the « installCommandDefinitionsFromStorageFileAsync » function. If everything it’s not OK the callback of the promise returns a error and you pass by:

console.error(‘error file vdmvoicecommands.xml’, er);

In this case: check and re check an re re re check your VCD file 🙂

If the file was initialized correctly, you could add a list of items to populate the phrase lists.



    var wap = Windows.ApplicationModel.Package;
    var voiceCommandManager = Windows.ApplicationModel.VoiceCommands.VoiceCommandDefinitionManager;

    var initCortana = function (categories) {
        categories = categories || [];
        return wap.current.installedLocation.getFileAsync("vdmvoicecommands.xml").then(function (file) {
            return voiceCommandManager.installCommandDefinitionsFromStorageFileAsync(file);
        }, function (er) {
            console.error('error file vdmvoicecommands.xml', er);
        }).then(function () {
           var language = window.navigator.userLanguage || window.navigator.language;

            var commandSetName = "VDM_" + language.toLowerCase();
            var commansets = Windows.ApplicationModel.VoiceCommands.VoiceCommandDefinitionManager.installedCommandDefinitions;
            if (commansets.hasKey(commandSetName)) {
                var vcd = commansets.lookup(commandSetName);
                var phraseList = [];
                categories.forEach(function (c) {
                    phraseList.push(c.title);
                });
                return vcd.setPhraseListAsync("cat", phraseList).then(function () {
                    console.log("VCD loaded !");
                 }, function (er) {
                    console.error('error set phraseList', er);
                })
            } else {
                console.warning("VCD not installed yet?");
            }
        }, function (ee) {
            console.error("installCommandDefinitionsFromStorageFileAsync error", ee);
        });
    }

Now you have to handle the activation event that will get sent to your app, and parse arguments.

  app.addEventListener("activated", function (args) {
        var appLaunchVoiceCommand = activation.ActivationKind.voiceCommand || 16;
        if (args.detail.kind === appLaunchVoiceCommand) {
            return handleVoiceCommand(args);
        }
    });

The handleVoiceCommand function parse the args passed from the activated app event and do the navigation to the right place

    var app = WinJS.Application;
    var commands = {
        "showlast": function (commandresult) {
            return WinJS.Navigation.navigate(VDM.Pages.VDMList, { viewType: 'last', viewLabel: WinJS.Resources.getString('appbar_views_last') });
        },
        "showcategorie": function (commandresult) {
            var categorie = commandresult.semanticInterpretation.properties.cat[0];
            return WinJS.Navigation.navigate(VDM.Pages.VDMList, { viewType: categorie.toLowerCase(), viewLabel: categorie });
        }
    }
    var handleVoiceCommand = function(args) {
        if (args.detail && args.detail.detail) {
            var voicecommand = args.detail.detail[0];
            if (voicecommand) {
                var result = voicecommand.result;

                if (result) {
                    var commandname = result.rulePath ? result.rulePath[0] : '';
                    var properties = result.semanticInterpretation ? result.semanticInterpretation.properties : {};
                    console.log("voice activation (" + commandname + ") confidence: " + result.rawConfidence, result);
                    var cmd = commands[commandname];
                    if (cmd) {
                        return cmd(result).then(function () {
                            VDM.Utils.loadMainAds();
                        });
                    }
                }

            }
        }
    }
    app.addEventListener("activated", function (args) {
        var appLaunchVoiceCommand = activation.ActivationKind.voiceCommand || 16;
        if (args.detail.kind === appLaunchVoiceCommand) {
            return handleVoiceCommand(args);
        }
    });

Let’s talk more with the app (with appService)

If you need a deeper integration with Cortana, you could also « talk » with her using an app service.

An app service is a Background task that Cortana could call when you use a command. You will have to explicitely declare which service Cortana must call in your command file.

    <Command Name="getFML">
      <Example> tell me a FML </Example>
      <ListenFor RequireAppName="ExplicitlySpecified"> tell me a {builtin:AppName} </ListenFor>
      <Feedback> Here an FML </Feedback>
      <VoiceCommandService Target="FMLVoiceCommandService"/>
    </Command>

Now let’s implement the Application Service. You must add it to your application manifest by pointing to JavaScript file, and give it the name you use in the command file :

 <uap:Extension Category="windows.personalAssistantLaunch"/>
 <uap:Extension Category="windows.appService" StartPage="js/voiceCommandService.js">
    <uap:AppService Name="FMLVoiceCommandService"/>
 </uap:Extension>

Beware the visual studio appxmanifest editor, it removes this entry if anything changes in it (like a upgrade of version when you generate a new appx package) this bug will certainly be corrected in the update 1 of Visual Studio.

Now let’s create the javascript file and implement the service itself.

When you are using JavaScript App Services are a lot like independant web workers. You can import all the JS file you need to run your code by using importScripts

importScripts("/WinJS/js/base.js");
importScripts("/js/FML.API.js");

The service is loaded by cortana, so when is loaded the doWork function is called.
If the Windows.UI.WebUI.WebUIBackgroundTaskInstance.current.triggerDetails is an instance of Windows.ApplicationModel.AppService.AppServiceTriggerDetails, we can get voice command used to launch this task and do things like:

  • Send an waiting response message
  • displays items
  • Send a final response message
var appService = Windows.ApplicationModel.AppService;
var backgroundTaskInstance = Windows.UI.WebUI.WebUIBackgroundTaskInstance.current;
var details = backgroundTaskInstance.triggerDetails;
var deferral = backgroundTaskInstance.getDeferral();

if (details instanceof appService.AppServiceTriggerDetails) {
    voiceServiceConnection = voiceCommands.VoiceCommandServiceConnection.fromAppServiceTriggerDetails(details);
    voiceServiceConnection.addEventListener("voiceCommandCompleted", onVoiceCommandCompleted);

    voiceServiceConnection.getVoiceCommandAsync().then(function completed(voiceCommand) {

    // here you can check the voiceCommand, call an API (or read a file) and send messages to Cortana UI

            var userMessage = new voiceCommands.VoiceCommandUserMessage();
                    userMessage.spokenMessage = "I'm Cortana and I read this awesome message";
                    userMessage.displayMessage = "I'm Cortana and I read this awesome message";
            var response = voiceCommands.VoiceCommandResponse.createResponse(userMessage);
            return voiceServiceConnection.reportSuccessAsync(response);

    });
}

The displayMessage string must not exceed 256 character

And now, with this, you can ask Cortana: « hey cortana, tell me a FML »

Bouton précédent (Back Button) avec le System Navigation Manager dans les applications (UWP) Windows 10

Dans les nouvelles applications Windows 10, nous avons la possibilité dans la version desktop d’une application d’utiliser le bouton de navigation (bouton précédent) du système qui est placé à coté du nom de l’application dans la barre de titre (comme celui qu’on trouve dans l’application de paramètres:

back

Ce bouton est nativement géré par WinJSContrib, il suffit pour cela d’activer l’option « enableSystemBackButton » :

 WinJSContrib.UI.enableSystemBackButton = true;

Un sample est disponible ici 🙂

Vous pouvez aussi l’implémenter manuellement. Il suffit d’utiliser la classe SystemNavigationManager et de modifier la propriété appViewBackButtonVisibility pour afficher ou cacher le bouton.

// il faut vérifier la disponibilité de l'api (disponible que sur desktop)
if (window.Windows && window.Windows.UI && Windows.UI.Core && Windows.UI.Core.SystemNavigationManager) {
    Windows.UI.Core.SystemNavigationManager.getForCurrentView().appViewBackButtonVisibility = Windows.UI.Core.AppViewBackButtonVisibility.visible;
    // ou pour cacher le bouton
    Windows.UI.Core.SystemNavigationManager.getForCurrentView().appViewBackButtonVisibility = Windows.UI.Core.AppViewBackButtonVisibility.collapsed;
}

Et pour gérer le clique il suffit de se brancher sur l’événement onbackrequested:

if (window.Windows && window.Windows.UI && Windows.UI.Core && Windows.UI.Core.SystemNavigationManager) {
    var systemNavigationManager = Windows.UI.Core.SystemNavigationManager.getForCurrentView();
    systemNavigationManager.onbackrequested = function (args) {
        if (WinJS.Navigation.canGoBack) {
            WinJS.Navigation.back();
            args.handled = true;
        } else {
            systemNavigationManager.appViewBackButtonVisibility = Windows.UI.Core.AppViewBackButtonVisibility.visible;
        }
    }
}

Pour la version en C# (et ma source) c’est par ici

Resolving WinJS webcomponents attributes

Now that you know how to use WinJS as webcomponents, and how to declare your controls, we will talk about the mecanism for resolving attribute values.

Lets work with an example :

<win-listview id="recentItemsList" member 
	itemdatasource="list:ctrl:recentItemsPromise" 
	itemtemplate="select:#pictureItem" 
	iteminvoked="event:ctrl:recentListItemInvoked"></win-listview>

You could think of attribute resolution as

[what to do]:[where to look]:[string token]

If you look at the « itemdatasource » attribute, you could read it like this : get the « recentItemsPromise » property from parent control, and wrap it in a WinJS.Binding.List for the ListView.

In this example, « recentItemsPromise » is just a string, and « ctrl » and « list » are operators. The list operator is smart enougth to see that it received a promise, and await it to set value.

Operators are just functions referenced in the namespace WinJSContrib.Utils.ValueParsers. You could provide your own operators by adding them to this namespace.

When the attribute is resolved, you will endup with something equivalent to :

list(domelement, ctrl(domelement, "recentItemsPromise"));

As you can see, their is no complex parsing involved, it is very efficient, and enforce composition. You have no limit at the number of operators you can chain like this.

You could find the list and details about built-in operators in the API documentation.

registering your WinJS webcomponents

In the last episode, we had a glimpse of using WinJS controls with webcomponents syntax, thanks to WinJS Contrib. Let’s dig a little deeper and see how you could use your own controls with it.

On platforms with non native implementation of webcomponents, the WinJS Contrib module is attaching an observer on each page controls (and releasing it on when disposing the page). This observer uses MutationObserver to track changes in the DOM and automatically build the WinJS controls that you registered. It means that controls, not on a page (like controls in default.html) are not instantiated. In such case, you must call « WinJSContrib.UI.WebComponents.inspect(element) » just like you would have to call WinJS.UI.processAll with vanilla WinJS.

Registering a control is just about providing the name of the DOM element, and how to map DOM attributes to your control implementation. If you don’t want to use DOM element attribute, you could still use data-win-options, or use both.

Declaring a control is quite easy. If you look in « winjscontrib.ui.webcomponents.js », you will find the declarations for WinJS controls. Lets look at the declaration for the DatePicker :

	WinJSContrib.UI.WebComponents.register('win-datepicker', WinJS.UI.DatePicker, {
		properties: ['calendar', 'datePattern', 'disabled', 'maxYear', 'minYear', 
				'monthPattern', 'yearPattern', 'onchange', 'current'],
		events : ['change']
	});	

You must call WinJSContrib.UI.WebComponents.register to wrap your control. You must at least provide the name of the DOM element, and the constructor for your control. The third argument is an object containing the mapping for your control. Within this object, the items in « properties » indicate the name of the properties that you want to map as attribute on the element, and « events » indicate the name of the events you want to map.

« properties » and « events » are shortcuts to build a mapping. You could provide a raw map if you have to do fancy stuf. Lets imagine that the calendar attribute used on DOM element must map to something like _calendarBuilder.calendar in your control. In such case you could use :

	WinJSContrib.UI.WebComponents.register('win-datepicker', WinJS.UI.DatePicker, {
		properties: ['datePattern', 'disabled', 'maxYear', 'minYear', 
				'monthPattern', 'yearPattern', 'onchange', 'current'],
		events : ['change'],
		map: {
			"CALENDAR" : {
				attribute : "calendar",
				property : "_calendarBuilder.calendar",
				type : "property",
				resolve : true
			},
			"CHANGE" : {
				attribute : "change",
				property : "_calendarBuilder.changeevent",
				type : "event",
				resolve : true
			}
		}
	});	

This syntax is more verbose but you have total control over the mapping. Note that the mapping key should be uppercase.

Another scenario that may come in mind is how do I map a fragment/page to a webcomponent ?

This is a lesser known feature of WinJS but WinJS.UI.Pages.define actually returns the constructor for the page, with the exact constructor signature of common WinJS controls (first parameters provides containing DOM element, and second parameter for control options).

To map a page/fragment to a component, you just have to pass the constructor. If you look at the searchbar control in the sample application, you will find an example that looks like this :

var SearchBarCtor = WinJS.UI.Pages.define("/controls/searchbar/searchbar.html", {
	//control implementation not mentionned here for brievity
});

//for use as standard control with data-win-control
WinJS.Namespace.define('Flickity.UI', { SearchBar: SearchBarCtor });

if (WinJSContrib.UI.WebComponents) {
   	WinJSContrib.UI.WebComponents.register('flickity-searchbar', SearchBarCtor);
}

The last use case we will cover now is for controls containing controls. Sometimes you write a control that wraps one or more other controls. You can map those controls directly to DOM attributes by using their own registration. WinJS Contrib has a control that wrap a SemanticZoom and the two associated ListView for managing list with groupings. The declaration for this control use that pattern :

WinJSContrib.UI.WebComponents.register('mcn-semanticlistviews', WinJSContrib.UI.SemanticListViews, {
	properties: [],
	controls: {
		"listview": WinJS.UI.ListView,
		"zoomedOutListview": WinJS.UI.ListView,
		"semanticZoom": WinJS.UI.SemanticZoom
	},
	map: {
		"DEFAULTGROUPLIMIT": { attribute: 'defaultGroupLimit', property: '_dataManager.defaultGroupLimit', resolve: true },
		"GROUPKIND": { attribute: 'groupKind', property: '_dataManager.groupKind', resolve: true },
		"FIELD": { attribute: 'field', property: '_dataManager.field', resolve: true },
		"ITEMS": { attribute: 'items', property: '_dataManager.items', resolve: true },
	}
});

The mapping can also have a « controls » section, which is used as a map. The key is the prefix for the control, and the value is the constructor for the control. When you register a control, the webcomponents module is creating a mapping and attach it to the constructor. That’s why you have nothing more to pass to map child control. However, the controls you are using as child map should have been declared before declaring this one.

The control can then be declared like this :

<mcn-semanticlistviews id="semanticzoom"
	listview.itemtemplate="select:#listItemTemplate"
	listview.groupheadertemplate="select:#groupItemTemplate"
	zoomedoutlistview.itemtemplate="select:#semanticItemTemplate"
	defaultGroupLimit="12"
	groupKind="global:WinJSContrib.UI.DataSources.Grouping.byField"
	field="metadata.genre"
	items="global:moviesSample">				
</mcn-semanticlistviews>

Next time we will have a look at the resolution of properties and detail the various options you have.