In the previous tutorial “creating a dianping Scope on Ubuntu OS (Qt JSON)”, we learned how to use C++ to develop a Scope on the Ubuntu platform; We also showed how to develop a Scope on Ubuntu using the Go language in the article “Designing Our Ubuntu Scope with Golang”. In today’s article, we’ll show you how to develop a Scope using the Javascript language. This is great news for web developers who can easily develop their own Scope without having to learn another language. More knowledge about the Scope and development can developer.ubuntu.com/en/scopes/ in the url

\

1) installation

First we must emphasize that Javascrip support for Scope has been developed since Ubuntu 15.04 (Vivid) and later versions. Before developing, developers must install their OWN SDK as described in the article “Ubuntu SDK Installation”. At the same time, the following JS Scope development tools must be installed:

\

$ sudo apt install unity-js-scopes-dev
$ unity-js-scopes-tool setup
Copy the code

It is important to note here that we cannot perform the above installation until we have installed our Ubuntu SDK, and chroots must be installed completely during the SDK installation. After the above installation, we have basically finished installing all of our tools.

\

\

2) JS Scope development document

\

\

All development is inseparable from the technical documentation we need. The JS Scope development documentation address can be found in early Build. You can also get help by installing the unity-js-scopes-doc package.

\

\

3) Create our Scope

\

Webservice API

\

Let’s use our previous baidu Weather API as an example. The connection to the API is:

\

Api.map.baidu.com/telematics/…

\

After clicking the link above, we get JSON output:

\

{" error ": 0," status ":" success ", "date" : "2016-01-18", "results" : [{" currentCity ":" Beijing ", "pm25" : "13", "index" : [{" title ":" dressing ", "zs" :" cold "," TIPT ":" dressing index "," DES ":" Cold weather, It is recommended to wear thick down jackets, fur coats and thick sweaters in the middle of winter. The elderly and infirm should pay special attention to warmth and anti-freezing." },{" tiPT ":" TIPT ":" DES ":" The weather is better, the temperature is a little lower, and the wind is a little stronger, so you feel a little cold, will have a certain effect on going out. "},{"title":" cold "," ZS ":" tiPT ":" cold index "," DES ":" Cold is easy to occur in cold weather, day and night temperature difference is great and the air humidity is larger, please pay attention to appropriate increase or decrease clothes, Strengthen self-protection to avoid colds. "},{"title":" exercise "," ZS ":" less suitable "," TIPT ":" exercise index "," DES ":" The weather is good, but considering the cold weather and strong wind, it is recommended that you do indoor exercise. If in the outdoors activities. Please be sure to keep warm and ready for "}, {" title ":" ultraviolet strength ", "zs" : "the weak", "tipt" : "uv intensity index", "des" : "ultraviolet intensity is weak, "}],"weather_data":[{"date":" Monday, January 18 () : - 8 ℃) ", "dayPictureUrl" : "http://api.map.baidu.com/images/weather/day/qing.png", "nightPictureUrl" : "http://api.map.baidu.com / images/weather/night/the qing. PNG ", "weather", "qing", "wind" : "the north wind 3-4", "temperature", "- 4 ~ - 11 ℃ "}, {" date ":" Tuesday ", "dayPictureUrl" : "http://api.map.baidu.com/images/weather/day/qing.png", "nightPictureUrl" : "http://api . Map.baidu.com/images/weather/night/duoyun.png ", "weather" : "clear to overcast", "wind" : "breeze", "temperature" : "1 ~ - 8 ℃ "}, {" date ":" on Wednesday ", "dayPictureUrl" : "http://api.map.baidu.com/images/weather/day/duoyun.png", "nightPictureUrl" : "http://ap I.map.baidu.com/images/weather/night/yin.png weather ", "cloudy" : "turn", "wind" : "breeze", "temperature" : "0 ~ - 7 ℃ "}, {" date ":" on Thursday, "" dayPictureUrl" : "http://api.map.baidu.com/images/weather/day/yin.png", "nightPictureUrl" : "http://api.m Ap.baidu.com/images/weather/night/duoyun.png ", "weather" : "cloudy to overcast", "wind" : "breeze", "temperature" : "3 ~ 6 ℃"}}}]]Copy the code

Our Scope needs to parse the jSON-formatted output above and render it in our Scope.

\

\

Create a basic scope

\

In this section, we create a JS Scope. We can easily create a Scope using the template provided in the Ubuntu SDK. First, we open our SDK and select “New File or Project” :

\

      \

\

\

   \

\

\

   \

\

   \

\

In the last few steps, we had to do the same for each Kit we selected to complete the project. We can now run our Scope (click the green button in the lower left corner of the SDK) :

\

\

\

The following information is displayed. Basically nothing special. By default, it displays a weather Scope in which we can enter the names of the cities we are interested in to get the weather conditions of the current city. We can choose SDK screen as the bottom corner of the Desktop or Ubuntu Desktop SDK Kit to run in the Desktop environment. When we need to run on mobile, we have to select Ubuntu SDK for ARMHF to run:

\

\

   \

\

\

Project Overview and NPM integration:

\

We have produced our project above, let’s first look at the structure of our project:

\

liuxg@liuxg:~/release/ ChinaWeatherjs $Tree. ├── Chinaweatherjs. Apparmor ├─ CMakelists.txt ├─ cmakelists.txt. User ├─ The manifest. Json. In ├ ─ ─ Po │ ├ ─ ─ chinaweatherjs. Pot │ ├ ─ ─ CMakeLists. TXT │ ├ ─ ─ a Makefile. In. In │ ├ ─ ─ POTFILES. In │ └ ─ ─ POTFILES. In. In └ ─ ─ the SRC ├ ─ ─ chinaweatherjs. Js ├ ─ ─ CMakeLists. TXT ├ ─ ─ data │ ├ ─ ─ chinaweatherjs. Ini. In │ ├ ─ ─ Chinaweatherjs - Settings. Ini. │ in ├ ─ ─ icon. The PNG │ └ ─ ─ logo. The PNG ├ ─ ─ etc └ ─ ─ node_modules ├ ─ ─ last - build - arch. TXT └ ─ ─ Unity - js - scopes ├ ─ ─ bin │ └ ─ ─ unity - js - scopes - the launcher ├ ─ ─ index. The js ├ ─ ─ lib │ └ ─ ─ the scope - core. Js └ ─ ─ unity_js_scopes_bindings.node 8 directories, 20 filesCopy the code

From the above structure we can see that our core file will be the SRC/chinaWeatherjs.js file. Node_modules contains the libraries we need. If you’ve done some Scope development before, it’s pretty easy to reuse this file to construct your own Scope. If you haven’t developed any other scopes, read on below.

\

NPM integration

\

Observant developers may have noticed a directory called node_modules. JS Scope uses the framework NPM + Scope. We can easily add NPM packages to our Scope project using the unity-js-scopes- scopes-tool. Run the following command:

\

$ unity-js-scopes-tool install <path/to/project/src/node_modules> <npm package> 
Copy the code

The above command will install any NPM packages we need into our project. If you are not familiar with NPM, please see the link to www.npmjs.com/.

\

\

API overview

In this section, we’ll take a look at the APIS we use and how to implement the Scope we need.

* * * *

The basic architecture of Javascript Scope

\

To be able to connect to the Scope runtime, your Scope only needs to follow a few simple rules:

  • Import the Javascript Scope module into your code
  • Set the Runtime context for your Scope

These steps are simply code like this:

\

var scopes = require('unity-js-scopes')
scopes.self.initialize({}, {});
Copy the code

Once imported, the Unity-JS-Scopes core module is the entry point for interaction with the Scope Runtime. The Runtime will help us set up our Scope, interact with our Dash, and display the results of user interaction within the Scope.

\

In the initialization code above, the “self” attribute is used to help us implement our interaction. It refers to the context of the currently running Scope. We can see the following code in the index.js file shown above: \

\

Object.defineProperty( module.exports, "self", { get: function() { if (! self) { self = new Scope(); } return self; }});Copy the code

In addition to defining some of the runtime elements of your Scope at runtime, your Runtime context allows you to check the current Scope Settings and accept changes produced when the Scope Runtime environment changes.

\

\

The Runtime element

\

Now we can revisit our Scope code and begin to define the behavior of some important runtime functions.

Once our Scope and Runtime are connected and started by the user, the Scope Runtime will send all actions generated by the user. Eventually these actions will be sent to API functions defined by our Scope during Initialize.

\

These API functions can be optionally defined by our Scope. They will reflect the most important triggered steps at Runtime. Here are some of the most important runtime callback functions.

\

  • Run: This callback function is called when a scope is ready to run.
  • Start: This function is called when a scope is ready to start
  • Stop: This function is called when a scope is ready to stop
  • Search: This function is called when the user requests a search. The Runtime will provide all the information needed for the search to the function call. The developer’s task is to push all possible results to RuntTime by interacting with the Runtime. You can also control how those results are displayed, right
  • Preview: Displays a preview that displays the results of the search above. Runtime will provide all the information needed for the preview

A simple template is:

\

var scopes = require('unity-js-scopes') scopes.self.initialize({}, { run: function() { console.log('Running... '); }, start: function(scope_id) { console.log('Starting scope id: ' + scope_id + ', ' + scopes.self.scope_config) }, search: function(canned_query, metadata) { return null }, preview: function(result, metadata) { return null }, }});Copy the code

For each scope Runtime callback function, it corresponds to a user interaction. The Scope Runtime wants your scope to send back an object that describes each key interaction.

For example, the search callback wants your scope to send back an object called SearchQuery. You will use this object to define the behavior of users when they conduct searches.

The SearchQuery Object can define a run callback function. This function is called when a search occurs. It can also define a callback function called Cancel. This function is called when a search is stopped.

The Scope Runtime also passes in an object called SearchReply. This object can be used to push results to the Scope Runtime.

\

The above interaction mode is the core interaction mode that runs through the whole scope and scope Rumtime design.

\

\

Push search results

\

One of the core search interactions mentioned above is that our Scope can push the results we need to the Scope Runtime. These results are pushed through SearchReply. This function expects data of type CategorisedResult to be created and pushed to the Scope Runtime. The Result object will let our scope define information such as title, icon, URI, and so on.

\

An additional feature of CategorisedResult is that when it is created, you can specify the layout to be displayed as a result. This layout is defined by the Category and CategoryRender objects. Here is an example of what we use in our weather scope. In order to obtain the data of Baidu weather API, we have to redefine the variables in Tempalate:

\

var query_host = "api.map.baidu.com"
var weather_path = "/telematics/v3/weather?output=json&ak=DdzwVcsGMoYpeg5xQlAFrXQt&location="
var URI = "http://www.weather.com.cn/html/weather/101010100.shtml";
Copy the code

The search method in Initialize is defined as follows:

\

                search: function(canned_query, metadata) {
                    return new scopes.lib.SearchQuery(
                                canned_query,
                                metadata,
                                // run
                                function(search_reply) {
                                    var qs = canned_query.query_string();
                                    if (!qs) {
                                        qs = "北京"
                                    }

                                    console.log("query string: " + qs);

                                    var weather_cb = function(response) {
                                        var res = '';

                                        // Another chunk of data has been recieved, so append it to res
                                        response.on('data', function(chunk) {
                                            res += chunk;
                                        });

                                        // The whole response has been recieved
                                        response.on('end', function() {
                                            // console.log("res: " + res);

                                            r = JSON.parse(res);

                                            // Let's get the detailed info
                                            var request_date = r.date
                                            console.log("date: " + date);

                                            var city = r.results[0].currentCity;
                                            console.log("city: " + city);

                                            var pm25 = r.results[0].pm25
                                            console.log("pm25: " + pm25)

                                            var category_renderer = new scopes.lib.CategoryRenderer(JSON.stringify(WEATHER_TEMPLATE));
                                            var category = search_reply.register_category("Chineweather", city, "", category_renderer);

                                            try {
                                                r = JSON.parse(res);
                                                var length = r.results[0].weather_data.length
                                                console.log("length: " + length)

                                                for (var i = 0; i < length; i++) {
                                                    var categorised_result = new scopes.lib.CategorisedResult(category);

                                                    var date = r.results[0].weather_data[i].date
                                                    console.log("date: "+  date);

                                                    var dayPictureUrl = r.results[0].weather_data[i].dayPictureUrl;
                                                    console.log("dayPictureUrl: " + dayPictureUrl);

                                                    var nightPictureUrl = r.results[0].weather_data[i].nightPictureUrl;
                                                    console.log("nightPictureUrl: " + nightPictureUrl);

                                                    var weather = r.results[0].weather_data[i].weather;
                                                    console.log("weather: " + weather);

                                                    var wind = r.results[0].weather_data[i].wind;
                                                    console.log("wind: " + wind);

                                                    var temperature = r.results[0].weather_data[i].temperature;
                                                    console.log("temperature: " + temperature);

                                                    categorised_result.set("weather", weather);
                                                    categorised_result.set("wind", wind);
                                                    categorised_result.set("temperature", temperature);

                                                    categorised_result.set_uri(URI);
                                                    categorised_result.set_title("白天: " + date );
                                                    categorised_result.set_art(dayPictureUrl);
                                                    categorised_result.set("subtitle", weather);
                                                    search_reply.push(categorised_result);

                                                    categorised_result.set_title("夜晚: " + date );
                                                    categorised_result.set_art(nightPictureUrl);
                                                    search_reply.push(categorised_result);

                                                }

                                                // We are done, call finished() on our search_reply
//                                              search_reply.finished();
                                            }
                                            catch(e) {
                                                // Forecast not available
                                                console.log("Forecast for '" + qs + "' is unavailable: " + e)
                                            }
                                        });
                                    }

                                    console.log("request string: " + query_host + weather_path + qs);

                                    http.request({host: query_host, path: weather_path + encode_utf8(qs)}, weather_cb).end();
                                },

                                // cancelled
                                function() {
                                });
                },
Copy the code

\

\

Preview search results

\

Once our search results are pushed to Scope Runtime and displayed, the user can click on the displayed results and request a Preview of the results. Scope Runtime will display the desired results through the Preview callback defined in your Scope.

\

As we described with Search above, The Scope Runtime expects your Scope to return a PreViewQuery object as a bridge to the interaction. This object must specify a run and a cancel function. These two functions have the same semantics as we described above in search. I will not repeat it here.

\

There are two most important elements for Preview: Column Layout and Preview Widgets. As their name suggests, the Column Layout elements are used to define the Layout of the Preview Component in the Preview page. Preview widgets are used to compose pages in the Preview page.

\

Once we understand the above, preview the plug-in and the association between the data it is bound to is done by “ID”. Here is the implementation of Preview in our Baidu weather: \

\

  preview: function(result, action_metadata) {
                    return new scopes.lib.PreviewQuery(
                                result,
                                action_metadata,
                                // run
                                function(preview_reply) {
                                    var layout1col = new scopes.lib.ColumnLayout(1);
                                    var layout2col = new scopes.lib.ColumnLayout(2);
                                    var layout3col = new scopes.lib.ColumnLayout(3);
                                    layout1col.add_column(["imageId", "headerId", "temperatureId", "windId"]);

                                    layout2col.add_column(["imageId"]);
                                    layout2col.add_column(["headerId", "temperatureId", "windId"]);

                                    layout3col.add_column(["imageId"]);
                                    layout3col.add_column(["headerId", "temperatureId", "windId"]);
                                    layout3col.add_column([]);

                                    preview_reply.register_layout([layout1col, layout2col, layout3col]);

                                    var header = new scopes.lib.PreviewWidget("headerId", "header");
                                    header.add_attribute_mapping("title", "title");
                                    header.add_attribute_mapping("subtitle", "subtitle");

                                    var image = new scopes.lib.PreviewWidget("imageId", "image");
                                    image.add_attribute_mapping("source", "art");

                                    var temperature = new scopes.lib.PreviewWidget("temperatureId", "text");
                                    temperature.add_attribute_mapping("text", "temperature");

                                    var wind = new scopes.lib.PreviewWidget("windId", "text");
                                    wind.add_attribute_mapping("text", "wind");

                                    preview_reply.push([image, header, temperature, wind ]);
                                    preview_reply.finished();
                                },
                                // cancelled
                                function() {
                                });
                }
Copy the code

We can run our Scope:

\

  \

\

\

\

We can deploy our Scope on our mobile phones in the following ways:

\

\

\

\

     \

\

The entire project source at: github.com/liu-xiao-gu…

\

For developers who want to learn the department’s Scope, you can watch our training video address. Video project source. Youtube video link.

To be able to compile the routines in the connection, we must go into our project and install Github as follows:

\

$ bzr branch lp:~davidc3/+junk/github-js-scope
$ liuxg@liuxg:~/scope/github-js-scope/src$ unity-js-scopes-tool install ./node_modules github 
Copy the code


\

\

\

    \

\