Giter Club home page Giter Club logo

tabris-js's Introduction

Tabris.js

Build Status Slack Status

Tabris.js is a framework for developing mobile apps with native UIs in JavaScript. iOS and Android apps can be built entirely from one code base, which frees you from the task of managing code for multiple platforms.

Tabris.js hello world

import {Button, contentView, TextView} from 'tabris';

// in JS

new Button({top: 16, centerX: true, text: 'Use native UI'})
  .onSelect(() => $(TextView).only().text = 'Powered by Tabris.js')
  .appendTo(contentView);
new TextView({top: 'prev() 16', centerX: true})
  .appendTo(contentView);

// or in JSX

contentView.append(
  <$>
    <Button top={16} centerX text='Use native UI'
            onSelect={() => $(TextView).only().text = 'Powered by Tabris.js'}/>
    <TextView top='prev() 16' centerX/>
  </$>
);

Native widgets

The code of the application is loaded dynamically - nothing is precompiled. JavaScript is executed Just-in-Time and passed via a native bridge to the device. Tabris.js accesses native controls and does not depend on webviews to render the app's UI. As a result, the performance of the apps cannot be distinguished from apps developed directly in native code of the platforms.

Getting started

To start developing Tabris.js applications, visit tabrisjs.com and check out the "Getting Started" guide in the documentation. Be sure to also consult the code snippets in the Tabris.js Developer App (download from the app store for Android and iOS).

Extensible

Tabris.js can be extended with Cordova plugins to add support for additional native features. A cordova plugin is also able to directly interface with the native widgets (as can be seen e.g. in the tabris-plugin-maps).

Additionally npm modules can be used to further enrich the available JS APIs.

Tabris.js also adds support for many key web technologies including:

  • Canvas
  • XMLHttpRequest / fetch()
  • WebSockets
  • localStorage

Build tabris npm module

Follow these steps if you want to build the tabris module yourself.

Install the Grunt build tool using npm:

npm install -g grunt-cli

In the tabris-js root directory fetch the dependencies and build:

npm install
grunt

License

Published under the terms of the BSD 3-Clause License.

tabris-js's People

Contributors

ahmadov avatar camstuart avatar cookieguru avatar cpetrov avatar davidweisscode avatar dbuschtoens avatar elshadsm avatar ifurnadjiev avatar irbull avatar jamessaidman avatar jkrause avatar jonek avatar jordiboehme avatar karolszafranski avatar mpost avatar nea avatar patrykmol avatar ralfstx avatar shaialon avatar tbuschto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tabris-js's Issues

Support creating widgets without parent

Currently, widgets can only be created in the append() method. It should also be possible to create widgets or composites without a parent and then append these widgets to a parent.

Enable access to service objects

Some services use client-side objects that aren't created by a protocol operation, e.g. Camera. There's currently no method to obtain a WidgetProxy for a service object.

Make proxy the function context when calling one of it's listeners

Currently there is no context ("this") set when a listener is called. This may become the only reason a widget reference needs to be stored in a local variable.

var widget = page.append( "Button", {...} ).on( "Selection", function(){ widget.foo(); } );

If the proxy is used as the context when the listener is called this is no longer needed:

page.append( "Button", {...} ).on( "Selection", function(){ this.foo(); } );

On the other hand this may be confusing for JS experts as well as beginners:
Beginner may think that "this" is available in nested functions also, or when using other frameworks that don't have this feature. For experts accessing "this" in an function not attached to an object may look alarming.

Provide abstraction for rwt.widgets.Grid

The mapping form a List to a grid is kind of ugly when it is combined with a Tabris UI because the grid has it's own navigation bar. We should hide the grid behind a simplified list api.

Support page actions

It should be possible to bind actions to a page, so that these actions are only visible while the page is active.

Reverse z-order of children

In SWT, the child that has been added last has the lowest z-index. This is counter-intuitive and also contrary to the DOM.

We should align with the DOM behavior (new elements are added on top). I think this can be changed directly on the clients. Since the RAP server also renders the children attribute, RWT will continue to work.

Support XmlHttpRequest via protocol message

A first draft of an abstraction for the protocol has been created:

the tabris.HttpRequest object is only used once and recycled after use. The tabris.js code will create a new object for each request. The snippet below reflects the latest comments added below.

We currently do NOT support the upload scenario but sketched the protocol messages for future consistency.

create {
    type: "tabris.HttpRequest"
}
    
call {
    method: "send"
    property: {
        method: string // "GET"
        url: string // "url.com/file.txt"
        headers: {string, string}
        data: string // request body data
        timeout: number // connection timeout in ms
        ...
    }
}

call {
    method: "abort"
}

listen {
    StateChange: boolean
}

notify "StateChange" {
    state: "uploading" | "uploadend" | "headers" | "loading" | "finished" | "aborted" | "error" | "timeout"
    code: number // http status code, send in "headers"
    message: string // http status message, send in "headers"
    headers: {string, string} // send in "headers"
    response: string // send in "finished"
}

listen {
    DownloadProgress: boolean
}

notify "DownloadProgress" {
    loaded: number,
    total: number,
    lengthComputable: boolean
}

listen {
    UploadProgress: boolean
}

notify "UploadProgress" {
    loaded: number,
    total: number,
    lengthComputable: boolean
}

Client crashes when calling an action without a function

Tabris.createAction() should either fail when the function is omitted or tabris.js should silently continue when the action is triggered. I prefer the latter since tabris.js should be forgiving as long as there is no real problem.

Separate public API from internal namespace object

Currently, we use the object Tabris both as public API and as namespace object for internal constructors such as Tabris.PageProxy. We should make a clear distinction between public and internal API.

I'd suggest that we use another object than Tabris as namespace. One obvious choice would be tabris, however, it's too easy to mistype Tabris as tabris which could then produce odd effects.

Support named colors

Currently, colors must be specified as array of numbers in the range 0..255, e.g. [255, 0, 0]. It should also be possible to use the basic color names like "red" or HTML hex notation (#ff0000).

Cannot obtain page size

It's currently not possible to obtain the actual page / display size in tabris.js (see automata.js demo).

Support font variants

Support selecting from different variants (weight, style, etc.) of a font family.

Introduce types to replace SWT style flags

Those SWT style flags that switch between widget types (PUSH, CHECK, SEPARATOR, etc.) should be replace with dedicated widget types. E.g., "Button", { style: [ "PUSH" ] } should be replaced by "PushButton".

Support asynchronous operations

All JS code currently runs in the native same thread that renders the UI. It should be possible to schedule functions to be executed at a later point.

One option would be to implement the functions setTimeout, setInterval etc. that are available in the browser.

Add support for ClientDevice service

The com.eclipsesource.tabris.device.ClientDevice allows a user to get various details about the device running the app.

These details are exposed via a regular service but the mechanism to obtain these infos differs from other services. In Tabris remote we send all these infos on the first POST request. Example:

["set", "tabris.Device", {
  "orientation": "PORTRAIT",
  "model": "Nexus 5",
  "osVersion": "4.4.4",
  "vendor": "LGE",
  "scaleFactor": 3.0,
  "connectionType": "WIFI",
  "timezoneOffset": -120,
  "capabilities": ["PHONE", "MESSAGE", "LOCATION", "CAMERA", "MAPS"]
}]

The best way to deal with that is properly to query this data in the fly as with other services. Opinions?

GC: fix method arc

Requires arc method in protocol, see eclipsesource/tabris-ios#484 and eclipsesource/tabris-android#567

The ClientDevice service is not populated with data because of missing first POST request

A remote tabris client would send an initial request like:

{"head":{"requestCounter":0,"rwt_initialize":true},"
operations":[
["set","w1",{"cursorLocation":[0,0],"bounds":[0,0,360,567],"colorDepth":24,"dpi":[160,160]}],
["set","tabris.Device",{"orientation":"PORTRAIT","model":"Nexus 5","osVersion":"4.4.4","vendor":"LGE","scaleFactor":3.0,"connectionType":"WIFI","timezoneOffset":-120,"capabilities":["PHONE","MESSAGE","LOCATION","CAMERA","MAPS"]}]

Sends there is no initial request, these information are missing.

Replace references to other widgets with widget id

When referencing another widget as a property, tabris.js should replace the other widget with the id. Example:

    tabFolder.append("TabItem", {
      index: 1,
      text: "Comments",
      control: tabRelatedComposite
    });

On the client side the tabRelatedComposite should be the widget id.

Add support to set the focus on a widget

In the protocol the focus is applied to a widget via a set on w1

["set","w1",{"focusControl":"w7"}]

Since the "display" is not exposed on tabris-js another mechanism has to be found. My expectation would be to have something like button.requestFocus(), button.focus=true or button.set("focus",true)

Provide function to retrigger a layout on a widget hierarchy

Currently we layout the tabris-js on creation. While that is fine there are situation where you change the layout later in the apps lifetime, where you have to relayout the ui.

Eg. set text on a label which would collapse to its intrinsic size if the text would become smaller.

DateTime widget needs to have several initial properties set

The snippet below shows what is send when creating a DateTime widget. Especially important are the year, month, day and pattern properties as the widget is empty on android by default.

["create", "w20", "rwt.widgets.DateTime", {
  "parent": "w6",
  "style": ["DATE", "MEDIUM", "BORDER"],
  "cellSize": [35, 20],
  "monthNames": ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", ""],
  "weekdayNames": ["", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
  "weekdayShortNames": ["", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
  "dateSeparator": "/",
  "datePattern": "MDY",
  "bounds": [111, 231, 234, 41],
  "children": [],
  "tabIndex": 9,
  "year": 2014,
  "month": 6,
  "day": 9,
  "subWidgetsBounds": [
    [0, 4, 10, 0, 19],
    [4, 4, 10, 0, 19],
    [2, 4, 10, 25, 19],
    [5, 29, 10, 7, 19],
    [1, 36, 10, 25, 19],
    [6, 61, 10, 7, 19],
    [3, 68, 10, 43, 19],
    [7, 202, 0, 30, 39],
    [13, 115, 0, 30, 39]
  ]
}]

Destroy Shells on Close event

The server answers with a destroy message to remove the shell. Here is the remote communication.

Client request:

{
  "head": {
    "requestCounter": 21
  },
  "operations": [
    ["notify", "w157", "Close", {}]
  ]
}

Server response:

{
  "head": {
    "requestCounter": 22
  },
  "operations": [
    ["destroy", "w157"],
    ["set", "w153", {
      "active": true,
      "defaultButton": "w154"
    }],
    ["listen", "w154", {
      "FocusOut": true
    }],
    ["set", "w1", {
      "focusControl": "w154"
    }]
  ]
}

GC: support clearRect

clearRect is currently not supported and cannot be emulated. The function sets all pixels in the specified rectangle to transparent black.

Closing a root page doesn't always work as expected

Closing a non-active root page for the first time makes it get opened, appearing with blank content. Every consecutive close() invocation works as expected.

Tabris.load(function() {

  var red = [210, 50, 20];
  var green = [0, 128, 0];

  var topLevelPages = [];

  var createRandomPageId = function() {
    return Math.floor(Math.random() * (99999) + 10000);
  };

  var createRootPage = function(pageTitle) {

    var page = Tabris.createPage({
      title: pageTitle,
      topLevel: true
    });

    var addRootPageButton = page.append("Button", {
      layoutData: {left: 5, right: 5, top: 5},
      text: "Add Root Page",
      background: green,
      foreground: [255, 255, 255]
    });

    addRootPageButton.on("Selection", function() {
      var page = createRootPage("Root Page: " + createRandomPageId());
      topLevelPages.push(page);
      topLevelPages[topLevelPages.length - 1].open();
    });

    var addPageButton = page.append("Button", {
      layoutData: {left: 5, right: 5, top: [addRootPageButton, 5]},
      text: "Add Page",
      background: green,
      foreground: [255, 255, 255]
    });

    var addGlobalActionButton = page.append("Button", {
      layoutData: {left: 5, right: 5, top: [addPageButton, 5]},
      text: "Add Global Action",
      background: green,
      foreground: [255, 255, 255]
    });

    page.append("Button", {
      layoutData: {left: 5, right: 5, top: [addGlobalActionButton, 5]},
      text: "Add Page Action",
      background: green,
      foreground: [255, 255, 255]
    });

    var removeLastPageActionButton = page.append("Button", {
      layoutData: {left: 5, right: 5, bottom: 5},
      text: "Remove Last Page Action",
      background: red,
      foreground: [255, 255, 255]
    });

    var removeLastGlobalActionButton = page.append("Button", {
      layoutData: {left: 5, right: 5, bottom: [removeLastPageActionButton, 5]},
      text: "Remove Last Global Action",
      background: red,
      foreground: [255, 255, 255]
    });

    var removeLastRootPageButton = page.append("Button", {
      layoutData: {left: 5, right: 5, bottom: [removeLastGlobalActionButton, 5]},
      text: "Remove Last Root Page",
      background: red,
      foreground: [255, 255, 255]
    });

    removeLastRootPageButton.on("Selection", function() {
      if (topLevelPages.length - 1 >= 0) {
        topLevelPages[topLevelPages.length - 1].close();
        topLevelPages.pop();
      }
    });

    return page;
  };

  var topLevelPage = createRootPage("Dynamic UI Start");
  topLevelPage.open();

});

Provide callback for widget resize

To be able to obtain the size of a page or a widget, we need a callback that is notified whenever the bounds of a widget change. The tabris clients support Resize events only on Display and Shell, however these are fired before the entire layout is rendered.

Without knowing the size of a canvas widget, there is no reasonable way to draw into this canvas.

Rename "path" property in index.json files

A current index.json file looks like the following:

{
  "resources": [
    {
      "path": "http://download.eclipsesource.com/technology/tabris/js/current/tabris.js"
    },
    {
      "path": "keyboard.js"
    }
  ]
}

The "path" property is not really fitting since an absolute url is not a path. I would propose to use "url" as an url can be relative or absolute.

Video autostart is too cumbersome

Setting the playback property on "play" at creation time does not work because the videoplayer is not in ready state at the time of creation. This can be overcome by adding a playbackListener and waiting for the "ready" state. However, this is prone to timing issues - you may miss the event if the ready state has been reached before the listener gets attached.

Possible solution: Buffer the playback property on creation time until the videoplayer is in ready state.

Invoking get("selection") on a button returns "undefined'

Tabris.load(function() {

  var page = Tabris.createPage({
    title: "Test",
    topLevel: true
  });

  var checkbox = page.append("Button", {
    layoutData: {
      left: 5,
      top: 5,
      height: 30,
      width: 110
    },
    style: ['CHECK'],
    text: "Controls",
    selection: true
  });

  var checkboxInfo = page.append("Label", {
    layoutData: {
      left: 5,
      top: [checkbox, 5],
      right: 5,
      height: 50
    },
    text: "Initial text"
  });

  checkbox.on("Selection", function() {
    checkboxInfo.set("text", "Enabled: " + checkbox.get("enabled") + "\nSelected: " + checkbox.get("selection"));
  });

  page.open();
});

Position of Group widget content needs to be adjusted

In tabris remote the position of group widget content is adjusted by the style information on the server side. Since that is not the case in tabris-js the group widget content starts to appear at 0,0.

The problem is similat to #30.

Snippet to reproduce:

tabris.load(function() {

  var page = tabris.createPage({
    title: "Group Example",
    topLevel: true
  });

  var group = page.append("rwt.widgets.Group", {
    text: "Group Widget",
    layoutData: {left: 0, top: 0, right: 0}
  });

  var label = group.append("rwt.widgets.Label", {
    text: "Label",
    layoutData: {left: 0, top: 0, right: 0}
  });

  page.open();

});

device-2014-07-22-164418

Facilitate the layout of widgets in a grid

Grid layouts are a common way to layout user interfaces. Our relative layout is very powerful, but not very intuitive when creating grids.

One option to improve this is to create some kind of GridFactory that can be initialized with the number of columns (and their width). Calling the GridFactory in the order the widgets are created would then create the appropriate layoutData.

 var text = page.append( "Text", {
    text: "some text",
    layoutData: gridFactory.next()
  });

Shells with TITLE style should account for the height of title when adding content

As described in this issue: https://github.com/eclipsesource/tabris-android/issues/540

The content is rendered on top of the title because the server previously set the top property according to the css details in the server thereby moving the content down.

So we could either move the content down in tabris-js as well or the client would need to distinguish between server and tabris-js run time environment and adjust the content manually which is not very nice.

Android back navigation broken

Using the Android back button currently leads to an exception. This bug has probably been introduced by commit f91f40c.

On a "ShowPreviousPage" event, the UIProxy calls close() on the Proxy for the page. However, this is a Proxy, not the PageProxy itself, so it does not have a close method. The PageProxy creates Proxy instances for Page and Composite and keeps the references internally.

The PageProxy should really be the proxy for the Page, instead of wrapping it. This may require some kind of inheritance.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.