pax_global_header 0000666 0000000 0000000 00000000064 12176226243 0014517 g ustar 00root root 0000000 0000000 52 comment=23e359df4bbcbed6a7ac8bf49c2f7923857e146a renderjs-23e359df4bbcbed6a7ac8bf49c2f7923857e146a/ 0000775 0000000 0000000 00000000000 12176226243 0020573 5 ustar 00root root 0000000 0000000 renderjs-23e359df4bbcbed6a7ac8bf49c2f7923857e146a/.gitignore 0000664 0000000 0000000 00000000016 12176226243 0022560 0 ustar 00root root 0000000 0000000 #vi *.swp *~ renderjs-23e359df4bbcbed6a7ac8bf49c2f7923857e146a/Makefile 0000664 0000000 0000000 00000004573 12176226243 0022244 0 ustar 00root root 0000000 0000000 # files RENDERJS = renderjs.js RENDERJS_MIN = renderjs.min.js BUILDDIR = tmp include config.mk all: external lint test build doc ######################################### # Download external lib ######################################### # Download all external libs external: lib/sinon/sinon.js \ lib/sinon/sinon-qunit.js \ lib/jquery/jquery.js \ lib/jschannel/jschannel.js \ lib/require/require.js \ lib/qunit/qunit.js \ lib/qunit/qunit.css \ lib/jio/jio.js \ lib/jio/md5.js \ lib/jio/complex_queries.js \ lib/jio/localstorage.js lib/sinon/sinon.js: @mkdir -p $(@D) curl -s -o $@ http://sinonjs.org/releases/sinon-1.7.3.js lib/sinon/sinon-qunit.js: @mkdir -p $(@D) # curl -s -o $@ http://sinonjs.org/releases/sinon-qunit-1.0.0.js curl -s -o $@ https://raw.github.com/jfromaniello/jmail/master/scripts/Tests/sinon-qunit-1.0.0.js lib/jquery/jquery.js: @mkdir -p $(@D) curl -s -o $@ http://code.jquery.com/jquery-2.0.3.js lib/jschannel/jschannel.js: @mkdir -p $(@D) curl -s -o $@ http://mozilla.github.io/jschannel/src/jschannel.js lib/require/require.js: @mkdir -p $(@D) curl -s -o $@ http://requirejs.org/docs/release/2.1.8/comments/require.js lib/qunit/qunit.%: @mkdir -p $(@D) curl -s -o $@ http://code.jquery.com/qunit/qunit-1.12.0$(suffix $@) lib/jio/jio.js: @mkdir -p $(@D) curl -s -o $@ http://git.erp5.org/gitweb/jio.git/blob_plain/refs/heads/master:/jio.js lib/jio/md5.js: @mkdir -p $(@D) curl -s -o $@ http://git.erp5.org/gitweb/jio.git/blob_plain/refs/heads/master:/lib/md5/md5.js lib/jio/localstorage.js: @mkdir -p $(@D) curl -s -o $@ http://git.erp5.org/gitweb/jio.git/blob_plain/refs/heads/master:/src/jio.storage/localstorage.js lib/jio/complex_queries.js: @mkdir -p $(@D) curl -s -o $@ http://git.erp5.org/gitweb/jio.git/blob_plain/refs/heads/master:/complex_queries.js $(RENDERJS_MIN): $(RENDERJS) $(UGLIFY_CMD) "$<" > "$@" ${BUILDDIR}/$(RENDERJS).lint: $(RENDERJS) test/renderjs_test2.js @mkdir -p $(@D) $(LINT_CMD) "$(RENDERJS)" $(LINT_CMD) "test/renderjs_test2.js" touch $@ ${BUILDDIR}/index.html.ok: test/index.html $(PHANTOMJS_CMD) ./test/run-qunit.js $< @mkdir -p $(@D) @sleep 1 touch $@ build: $(RENDERJS_MIN) test: ${BUILDDIR}/index.html.ok lint: ${BUILDDIR}/$(RENDERJS).lint doc: $(YUIDOC_CMD) . clean: rm -rf $(RENDERJS_MIN) ${BUILDDIR} lib/sinon lib/jquery lib/jschannel lib/qunit lib/jio lib/require renderjs-23e359df4bbcbed6a7ac8bf49c2f7923857e146a/README 0000664 0000000 0000000 00000001335 12176226243 0021455 0 ustar 00root root 0000000 0000000 What is RenderJs? RenderJs is a JavaScript library which uses jQuery and RequireJS. RenderJs provides an easy way to define gadgets (aka mashups) in pure HTML5 and does not require application server. It handles dependencies through RequireJS, caching and interaction. It is suitable for the development of mobile applications, desktop applications. It is used by OfficeJS, ERP5. Documentation Documentation is available at http://www.renderjs.org/ Developer > git clone https://git.erp5.org/repos/renderjs.git > make (will produce a minified version for now, requires nodejs installed as well its uglifyjs module) > make lint (will produce a jslint report, requires nodejs installed as well its jslint module) renderjs-23e359df4bbcbed6a7ac8bf49c2f7923857e146a/TODO 0000664 0000000 0000000 00000000323 12176226243 0021261 0 ustar 00root root 0000000 0000000 handle relative url #parseGadgetHTML TODO how to manage local script tag #parseGadgetHTML TODO check that gadget/dom context is kept in promise TODO keep css file media query #declareCSS TODO test selector TODO renderjs-23e359df4bbcbed6a7ac8bf49c2f7923857e146a/config.mk 0000664 0000000 0000000 00000000445 12176226243 0022374 0 ustar 00root root 0000000 0000000 # npm install uglify-js UGLIFY_CMD = $(shell which uglifyjs || echo node ~/node_modules/uglify-js/bin/uglifyjs) # npm install jslint LINT_CMD = $(shell which jslint || echo node ~/node_modules/jslint/bin/jslint.js) --terse YUIDOC_CMD = $(shell which yuidoc) PHANTOMJS_CMD = xvfb-run phantomjs renderjs-23e359df4bbcbed6a7ac8bf49c2f7923857e146a/doc.html 0000664 0000000 0000000 00000127254 12176226243 0022241 0 ustar 00root root 0000000 0000000
To be generated
renderJS is a JavaScript library which helps modularizing your HTML5 code and your JavaScript applications. It uses the HTML5 standard as an easy way to specify self-contained components which consist of HTML5 code, CSS code and Javascript code. It helps reusing components in different applications just as easily as linking an HTML5 page to another. It also helps combining multiple reusable component within the same single page application.
The main focus of renderJS is graphic user interface (GUI) components such as editors, viewers, fields or widgets. renderJS can also be used to define reusable components with no GUI such as translators or format converters.
Let us suppose we want to add a text editor to an HTML5 page, here is how we proceed:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Simple example</title>
<script src="jquery.js" type="text/javascript"></script>
<script src="render.js" type="text/javascript"></script>
</head>
<body>
<p>Text editor displays below:</p>
<div data-gadget-path="http://www.renderjs.org/editor/rtl/index.html"></div>
</body>
</html>
renderJS takes care if managing all dependencies and rendering.
renderJS is separated into 3 parts: renderJS library, renderJS interfaces and renderJS core gadgets.
renderJS library provides a simple way to load inside an existing HTML5 page third-party components which follow the renderJS specification. It implements a recursive macro language in pure HTML5. This macro language provides a way to embed and combine recursively components from different sources.
renderJS core interfaces (http://www.renderjs.org/interface/) provide the documentation to common interfaces shared by all renderJS components. It defines in particular the renderable interface for all components which can be rendered in a page such as editors, viewers, fields or widgets.
renderJS core gadgets (http://www.renderjs.org/gadget/core/) provide a reference implementation of renderJS interfaces. It provides in particular the default implementation of renderJS router and renderJS catalog. renderJS router is used to manage interactions between user and components or between components themselves. renderJS catalog provides a way to search for components which meet a given interface.
This walkthrough is designed to get you started using a basic renderJS instance.
Download renderJS library, renderJS core as well as the dependencies required to start using core components.
Script |
Description |
Source |
---|---|---|
renderJS |
The renderJS library |
render.js |
jquery |
The JQuery library version XXX |
jquery.js |
Add the renderJS script to your HTML page:
<script src="jquery.js" type="text/javascript"></script>
<script src="render.js" type="text/javascript"></script>
renderJS can be used either by adding HTML5 tags to your page or by invoking Javascript methods.
The renderJS script provides 10 HTML5 tags which can be added to any HTML5 page.
Method |
Sample Call |
Description |
---|---|---|
data-gadget-path |
<div data-gadget-path="hello-world.html"></div>
|
Embeds the HTML5 code of a gadget in a page, loads CSS and Javascript depencies and call render() method on the gadget if it is defined. |
data-gadget-slot |
<div data-gadget-slot="main">
|
Defines the porting of the DOM tree in the HTML5 code of a gadget which is going to be replaced after render() method is called |
data-gadget-fill |
<div data-gadget-path="hello-layout.html">
|
Fills given slot in DOM parent gadget with HTML5 content defined in DOM child tree. Filling is applied recursively. |
data-gadget-scope |
<div data-gadget-path="hello-layout.html" data-gadget-scope="hello"></div>
|
Associates a gadget to a namespace which can later be used to retrieve a gadget by URL or programmatically |
data-gadget-state |
<div data-gadget-path="html-viewer.html" data-gadget-state="{'text_content':'<p>Some content</p>'"></div>
|
Defines the state of the gadget as serialized JSON string with properties. This state can be used to implement persistency of a gadget and ability to move it from one page to another. |
data-gadget-cache |
<div data-gadget-path="hello-layout.html" data-gadget-cache="form"></div>
|
Defines the caching policy of a gadget. Caching itself is implemented by a "Cache services" gadget. |
data-gadget-sandbox |
<div data-gadget-path="hello-layout.html" data-gadget-sandbox="iframe"></div>
|
Defines the sandboxing type of a gadget (iframe, safejs, etc.). |
data-gadget-public |
<div data-gadget-path="hello-layout.html" data-gadget-public="bang"></div>
|
Declares which methods or events of a gadget are public. |
data-gadget-routed |
<div data-gadget-path="hello-layout.html" data-gadget-routed="a.someMethod:bang; b.anotherEvent:bang"></div>
|
Declares which methods or events of a gadget are forwarded to the router. |
The renderJS Javascript API replicates and extends HTML5 tags.
Method |
Parameters |
Returns |
Description |
---|---|---|---|
€(scope_selector) |
selector
|
Gadget selection. |
Select all gadgets matching the scope selector. |
€(scope_selector).declareGadget OR $(selector).declareGadget |
|
Promise (for callback) |
Declares that a given gadget with given parameters should exist as the parent of another gadget and should be attached to a given DOM position. |
€(scope_selector).remove |
scope
|
Promise (for callback) |
Remove the set of matched gadgets |
€(scope_selector).declareMethod |
|
Chainable gagdet selector |
Declares a public method of a gadget. |
We will learn here how to create a gadget, how to use a gadget and how to make gadgets interact.
Many people just want to use renderJS in order to display and edit content and leverage the rich library of gadgets of OfficeJS which renderJS is based.
Here how to do.
Let us add an HTML document to our web server in /var/www/html/hello_viewer/test.html
Let us add an SVG document to our web server in /var/www/html/hello_viewer/test.svg
We are going to display both documents in the page using whichever editor is available to display it.
We create the HTML file /var/www/html/hello_viewer/test.html
with the following code.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Hello Viewer Application</title> <script src="jquery.js" type="text/javascript"></script> <script src="render.js" type="text/javascript"></script> </head> <body> <!-- Warning message if no javascript --> <noscript>Please turn on Javascript.</noscript> <!-- Include two universal viewers --> <p>Display first viewer with HTML file</p> <div data-gadget-path="http://officejs.org/editor/universal/index.html" data-gadget-sandbox="iframe" data-gadget-state="{'src': '/hello_viewer/test.html'}" /> <p>Display second viewer with SVG file</p> <div data-gadget-path="http://officejs.org/editor/universal/index.html" data-gadget-sandbox="iframe" data-gadget-state="{'src': '/hello_viewer/test.svg'}" /> </body> </html>
Here is the result:
[DRAWING]We only use here a single gadget: the universal editor. This gadget has two instances. Each instance is able to download a file, guess its mime type and select
the most appropriate viewer to display it. It removes the burden to select a viewer from the developer. We have used the tag data-gadget-sandbox
on
both gadgets in order to make sure that an iframe sandbox prevent CSS conflicts which most viewers generate because of poor namespacing.
Let us now create a simple gadget from scratch. This gadget displays the text "Hello World". We first create a directory in our web server.
/var/www/html/gadget/hello_world
Then we create the file /var/www/html/gadget/hello_world/index.html
that will contain the gadget code.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Hello World</title> </head> <body> <p>Hello World</p> </body> </html>
We can display the gadget by opening the URL
http://localhost/gadget/hello_world/index.html
Which displays the "Hello World" test in a blank page:
[DRAWING]We will now embed the "Hello World" the gadget in a renderJS application.
Let us create the file /var/www/html/gadget/hello_application/index.html
which will contain the HTML5 application.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Hello World Application</title> <script src="jquery.js" type="text/javascript"></script> <script src="render.js" type="text/javascript"></script> </head> <body> <!-- Warning message if no javascript --> <noscript>Please turn on Javascript.</noscript> <!-- Include Hello World gadget --> <div data-gadget-path="../hello_world/index.html" /> </body> </html>
Let us now check the result:
[DRAWING]And the HTML5 code which was generated:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Hello World Application</title> <script src="jquery.js" type="text/javascript"></script> <script src="render.js" type="text/javascript"></script> </head/> <body/> <!-- Warning message if no javascript --> <noscript>Please turn on Javascript.</noscript> <!-- Include Hello World gadget --> <div data-gadget-path="../hello_world/index.html" data-gadget-scope="Hello World"> <p>Hello World</p> </div> </body> </html>
This example shows the basic feature of renderJS: the ability to dynamically include in a page code defined in another page. You can consider in this example that renderJS is a kind of macro system which runs on the client side rather than on the server side. You will also take note that renderJS generated the tag "data-gadget-scope" automatically by taking the default value provided by the title of the gadget.
Let us now add some behaviour to our gadget. We create a script /var/www/html/gadget/hello_world/hello.js
with the following code
€(this).declareMethod("foo", function () { // replace body paragraph with Bang! word $(this).find('p').text("Bang!"); });
We now need to attach a dependency to this script in the gadget definition. We are going also to use the data-gadget-public
tag.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Hello World</title> <script src="jquery.js" type="text/javascript"></script> <script src="render.js" type="text/javascript"></script> <script src="hello.js" type="text/javascript"></script> </head> <body data-gadget-public="foo"> <p>Hello World</p> </body> </html>
If we open our Web browser to http://localhost/gadget/hello_world/index.html
, we can view the Hello Word gadget itself:
Now, if we type the URL http://localhost/gadget/hello_world/index.html#foo
, we get something new:
What happens here is that the data-gadget-public
has declared the method foo
as a public method that can be invoked through a
hashtag. This is simular to the concept of method publication in application servers such as Zope, Flash.
http://localhost/gadget/hello_application/index.html#hello/foo
. Here is the result:
The content "Hello World" was replaced by "Bang!". This example shows the concept of recursive publication of gadgets provided by renderJS. By using the
data-gadget-scope
tag, every public method of a gadget can be accessed through a simple URL.
We will now show a more complex example to illustrate the advanced macro capabilities of renderJS and the concept of interactor.
Let us create a slider field where the slider widget itself can contain any gadgets, such as forms, date fields, animated sprite, etc.
[DRAWING]Let us first define the HTML5 code of the gadget in /var/www/gadget/slider/index.html
<html> <!-- Define dependencies and supported interfaces --> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Slider Gadget</title> <script src="jquery.js" type="text/javascript"></script> <script src="render.js" type="text/javascript"></script> <link rel="stylesheet" src="slider.css"/> <script src="slider.js" type="text/javascript"></script> <link rel="http://www.renderjs.org/rel/interface" src="http://www.renderjs.org/interface/renderable"/> <link rel="http://www.renderjs.org/rel/interface" src="http://www.renderjs.org/interface/field"/> </head> <!-- Sample display made by HTML designer --> <body> <noscript>Please turn on Javascript.</noscript> <!-- Sample display made by HTML designer --> <div class="slider"> <div class="slider_element"> <!-- The content of this div is replaced dynamically by the call to render() --> <div data-gadget-slot="main"> <!-- main is the default --> <!-- This part is destroyed and replaced by render() --> <form> <input type="text" /> </form> </div> </div> </div> </body> </html>
We can now define the CSS of slider.css
:
.slider { background-color: black; display: inline-block; height: 0.5em; margin: 0.5em 0; position: relative; width: 20em; } .slider_element { background-clip: padding-box; background-color: #A1A1A1; border-radius: 0.75em 0.75em 0.75em 0.75em; border-width: 0; cursor: pointer; left: 50%; line-height: 1em; padding: 0.25em; position: absolute; top: -1em; } form input { cursor: pointer; max-width: 2em; }
The Javascript code of slider.js
looks like this:
XXX TO BE COMPLETED
This example introduces some new concepts of renderJS. The data-gadget-slot
slot defines a placeholder in the gadget which can be filled with
another gadget at render time. It is now filled with a single form with a single field. We will show later how to dynamically replace this single form with two
forms.
Another important concept introduced in this example is the notion of interface. The relation type http://www.renderjs.org/rel/interface
defines
an interface relation, following the concepts of link relation used increasingly in modern REST applications based on the HATEOAS approach. We define here that our gadget follows the renderable interface and the field interface.
Interfaces are self-documented online:
# http://www.renderjs.org/interface/renderable A gadget is meant to be rendered as part of the DOM tree of an HTML5 page. €(selector).render() [returns Promise] Renders the gadget in the given DOM context.
# http://www.renderjs.org/interface/field A renderJS gadget which provides a way for user to display information and collect information. €(selector).getValue() [returns Promise] Returns the value of given gadget. €(selector).setValue(value) [returns Promise] * value -- value to set Sets the value of a given gadget.
Let us now create a small application which embeds two forms inside a slider gadget and created an interaction between forms and the slider.
We should first create a form gadget in /var/www/gadget/slider/form.html
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Form Gadget</title> <script src="jquery.js" type="text/javascript"></script> <script src="render.js" type="text/javascript"></script> <script src="form.js" type="text/javascript"></script> <link rel="http://www.renderjs.org/rel/interface" src="http://www.renderjs.org/interface/renderable"/> <link rel="http://www.renderjs.org/rel/interface" src="http://www.renderjs.org/interface/field"/> </head> <body data-gadget-public="setValue"> <form> <input type="text" /> </form> </body> </html>
and define the form.js
script which implements the field API for the form itself.
€(this) .declareMethod("getValue", function () { // Deferred object is returned automatically by renderJS return $(this).find('input').val(); }) .declareMethod("setValue", function (value) { // Deferred object is returned automatically by renderJS $(this).find('input').val(value); });
It is time now to assemble all parts into an application: a slide gadget and two instances of the form in /var/www/gadget/slider/example.html
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Slider Example Application</title> <script src="jquery.js" type="text/javascript"></script> <script src="render.js" type="text/javascript"></script> </head> <body> <!-- Warning message if no javascript --> <noscript>Please turn on Javascript.</noscript> <!-- Page structure --> <div data-gadget-path="slider.html"> <div data-gadget-fill="main"> <!-- First Form --> <div data-gadget-path="form.html" data-gadget-scope="first"/> <!-- Second Form --> <div data-gadget-path="form.html" data-gadget-scope="second"/> </div> </div> </body> </html>
This example shows a typical example of the data-gadget-scope
tag in order to distinguish to instances of the same gadget. It also shows how the
data-gadget-fill
tag operates by filling the data-gadget-slot
named "main" in the Slider gadget.
Let us display the result by opening the URL http://localhost/gadget/slider/sample.html
:
Let us illustrate how the data-gadget-public
tag works in combination with data-gadget-scope
.
And if we call http://localhost/gadget/slider/sample.html#first/setValue?value=3
[DISPLAY]
The second form changes with value set to 3.
If we call http://localhost/gadget/slider/sample.html#second/setValue?value=7
[DISPLAY]
The second form changes with value set to 7.
We will now make the slider and forms interact so that moving the slider updates both forms and changing any form moves the slider. In order to achieve this, we need to add a interactor gadget to the application. The code of the application will look like this.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Slider Example Application</title> <script src="jquery.js" type="text/javascript"></script> <script src="render.js" type="text/javascript"></script> </head> <body> <!-- Warning message if no javascript --> <noscript>Please turn on Javascript.</noscript> <!-- Page structure --> <div data-gadget-path="slider.html" data-gadget-scope="slider" data-gadget-routed="setValue:interactor/notifyUpdatedValue?value={$value}" > <div data-gadget-fill="main"> <!-- First Form --> <div data-gadget-path="form.html" data-gadget-scope="first" data-gadget-routed="setValue:interactor/notifyUpdatedValue?value={$value}" /> <!-- Second Form --> <div data-gadget-path="form.html" data-gadget-scope="second" data-gadget-routed="setValue:interactor/notifyUpdatedValue?value={$value}" /> </div> </div> <div data-gadget-path="interactor.html" data-gadget-scope="interactor" data-gadget-public="notifyUpdatedValue" /> </body> </html>
What we just did here is to use the tag data-gadget-public
which simulates the call to a URL each time a method of a gadget is invokes. For
example, if setValue
is invoked on the first single field form, then we simulate a call to the URL interactor/notifyUpdatedValue
and
pass to it the parameters of setValue which ere interested in.
We can now define the interactor HTML5 code for interactor.html
:
<html/> <head/> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Slide Interactor</title> <script src="jquery.js" type="text/javascript"></script> <script src="render.js" type="text/javascript"></script> <script src="interactor.js" type="text/javascript"></script> </head/> <body/> <!-- Warning message if no javascript --/> <noscript>Please turn on Javascript.</noscript> </body/> </html/>
and its javascript code:
€(this).declareMethod("notifyUpdatedValue", function (value) { var slider = €("slider"), first = slider.find("first"), second = slider.find("second"), slider.getValue().done(function (current_value) { if (current_value !== value) { slider.setValue(value); } }); first.getValue().done(function (current_value) { if (current_value !== value) { first.setValue(value); } }); second.getValue().done(function (current_value) { if (current_value !== value) { second.setValue(value); } }); });
Now each time the user moves the slider, the forms are updated and each time forms are updated, the slider is moved.
TBD, mainly by calling declareGadget, declarePublic and declareRouted
TBD
TBD
TBD
TBD
TBD
TBD
TBD
Makes a given method of the gadget accessible through the router.
The simple form does not require any parameter:
<div data-gadget-path="a.html" data-gadget-public="someMethodOfA" data-gadget-scope="a"/>
can be invoked by a GET as:
#a/someMethodOfA
A more complex form adds name translation:
<div data-gadget-path="a.html" data-gadget-public="someOtherName:someMethodOfA" data-gadget-scope="a"/>
can be invoked by a GET as:
#a/someOtherName
It is also possible to publish a method as a POST:
<div data-gadget-path="a.html" data-gadget-public="POST:someOtherName:someMethodOfA" data-gadget-scope="a"/>
can be invoked by a POST on the page with _route parameter set in the HTTP request to.
_route: a/someOtherName
Routes a given method or event to the router.
In the first example, the interactor gadget defines routes on other gadgets. It is the preferred approach in terms of encapsulation but may cause security issues in the future.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Sample Application with 2 gadgets and interactor</title> <script src="jquery.js" type="text/javascript"></script> <script src="render.js" type="text/javascript"></script> </head> <body> <!-- Warning message if no javascript --> <noscript>Please turn on Javascript.</noscript> <!-- Page structure --> <div data-gadget-path="a.html" data-gadget-scope="a"/> <div data-gadget-path="b.html" data-gadget-scope="b"/> <!-- tell the router that calling browser://interact/bang should call bang --> <!-- wrap on a.someMehod a call to browser://interact/bang --> <!-- wrap on b.anotherEvent a call to browser://interact/bang --> <div data-gadget-path="interactor.html" data-gadget-scope="interactor" data-gadget-public="bang:bang; " data-gadget-routed="a.someMethod:bang; b.anotherEvent:bang"> </div> </body> </html>
In the second example, each gadget defines its own routes.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Sample Application with 2 gadgets and interactor</title> <script src="jquery.js" type="text/javascript"></script> <script src="render.js" type="text/javascript"></script> </head> <body> <!-- Warning message if no javascript --> <noscript>Please turn on Javascript.</noscript> <!-- Page structure --> <div data-gadget-path="a.html" data-gadget-scope="a" data-gadget-routed="someMethod:interactor/bang"/> <div data-gadget-path="b.html" data-gadget-scope="b" data-gadget-routed="someMethod:interactor/bang"/> <!-- tell the router that calling browser://interact/bang should call bang --> <!-- wrap on a.someMehod a call to browser://interact/bang --> <!-- wrap on b.anotherEvent a call to browser://interact/bang --> <div data-gadget-path="interactor.html" data-gadget-scope="interactor" data-gadget-public="bang:bang;"> </div> </body> </html>
renderJS core consists of 3 global methods.
€(scope_selector).declareGadget OR $(selector).declareGadget * url -- the path of the gadget definition ex. "slide-gadget.html' * [settings] // A set of key/value pairs * state -- the state of the gadget (JSON) * scope -- it defines the URL path where the gadget is publishable and the way to retrieve for its parent * position -- useful to try to order gadgets inside an element * sandbox -- the type of sandboxing (iframe, safefs) Declares the existence of a gadget by creating or updating it if necessary. €(scope_selector) Select all gadgets matching the scope selector. €(scope_selector).remove Remove the set of matched gadgets
renderJS provides a standard way to define interfaces using text files.
#http://standard/path/to/interface Some description of the interface in plain English in one or more lines of text. methodName * parameter1 -- description [OPTIONAL] * parameter2 -- description [OPTIONAL] Description of the method in in one or more lines of text. anotherMethodName * parameter1 -- description [OPTIONAL] * parameter2 -- description [OPTIONAL] Description of the method in in one or more lines of text.
All interfaces should returns promise because everything in renderJS is asynchronous.
# http://www.renderjs.org/interface/gadget The Gadget interface defines gadget introspection. It should be implemented by every gadget. getInterfaceList() Return the list of URL of interfaces of a gadget getPath() Returns the path of the HTML5 code of a gadget getTitle() Returns the title of a gadget getRequiredCSSList() Returns a list of CSS required by the gadget getRequiredJSList() Returns a list of JS required by the gadget
The Catalog interface defines how to search gadgets badget on gadget metadata.
# http://www.renderjs.org/interface/catalog The Catalog interface defines a single method to search gadgets based on their metadata. Its API mimics the API of JIO. allDocs(filter) * filter -- JSON dict which defines a predicate on metadata allDocs returns a list of dictionnaries of the form: { "path" : "http://some/path/to/the/gadget/source", "title" : "Gadget Title", "link" : "http://some/link/to/the/gadget", "interface" : "http://officejs.org/interface/field", } Additional parameters can be added to the list in order to extend the Catalog. For example, mime types and a priority could be added: { "path" : "http://some/path/to/the/gadget/source", "title" : "Gadget Title", "link" : "http://some/link/to/the/gadget", "interface" : "http://officejs.org/interface/field", "mime_type" : "text/svg", "priority" : 3, }
# http://www.renderjs.org/interface/router The Router interface describes how to make gadgets communicate and interact eachother. It is based on the idea of link and the possibility for two gadgets located in different sandboxes or on different runtime to communicate. makeLink(gadget, method, link_type) * gadget -- a gadget or a handle to a gadget * method -- the method of the gadget or the method ID [OPTIONAL] * link_type -- the type of link (GET, POST, PUT).a list of mime types which the gadget supports at least on certain platforms. This method can be used to build a platform independent catalog of viewer gadgets.
# http://www.renderjs.org/interface/editor The Editor interface provides a way to interact with content in real time through menu actions. It enables different editors with different menus to share more commonality in their appearance. setEditableState * editable -- true or false Turns on or off the ability to edit content. setMenuVisibility * visibility -- true or false Shows of hides the default menu of the editor. getMenuItemTree Returns the tree of menu actions supported by the editor. Each menu action is described as a JSON dict with the following format. { "id": "open", "title": "Open...", "dialog": true, } triggerAction * action Simulates an action on a menu item. notifyChange * location -- the location of the content * value -- the value of the content which is changed Each time some content is changes (title, paragraph, etc.) notifyChange is invoked with two parameters, once which describes the location of the content which was changed and the other the new value if any. If undefined value is passed, it is considered as a way to specify that location was deleted. setContent * location -- the location of the content * value -- the value of the content which is changed Updates content (by merging if needed).
# http://www.renderjs.org/interface/text-editor The Text Editor interface provides common text processing features. searchAndReplace * search_pattern -- * replace_pattern -- Replaces one string by another. getParagraphItemList * paragraph_type -- can be chapter or an HTML tag Returns a list of JSON dicts which describe each chapter in a text editor. This is useful for example to display a list of chapters outside the text editor. goToPosition * position -- the position to set the editor to. Position is an arbitrary value defined by getParagraphItemList Moves text editor to a given position.
# http://www.renderjs.org/interface/image-editor The Table Editor interface provides a simple way to access or change lines, columns and cells in a table.
Any application needs at least a gadget catalog and a router. By default, renderJS creates a standard router and a standard catalog. It is however possible to overload default services with your own as shown in the example below.
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Gadget Container Slider </title> <script src="jquery.js" type="text/javascript"></script> <script src="render.js" type="text/javascript"></script> </head> <body> <!-- Warning message if no javascript --> <noscript>Please turn on Javascript.</noscript> <!-- Overload dummy router and catalog with dummy ones --> <div data-gadget="dummy-router.html" data-gadget-scope="router"/> <div data-gadget="dummy-catalog.html" data-gadget-scope="catalog"/> </body> </html>
By using appropriate scope, the standard catalog and router are overloaded with dummy implementations which... do nothing.
The router is where everything happens to convert method calls into link invocations emissions and link invocations into method calls, both locally and remotely. It is key to communication between sandboxes in the same page, between URLs and gadgets and between gadgets on different hosts.
The makeHandle method converts a URL of a link to a handle. A handle is a proxy object capable of converting any method call to an AJAX POST. For performance reasons, gadgets in the same sandbox can access eachother directly, without conversion to link of method calls. Yet, the proxying is kept to enforce clean development.
// Get the router which is just a gadget var router = €("router"), // Get a handle to a remote gadget handle = router.makeHandle('http://some.server/some/scope/some/gadget'); // Call a method on the remote gadget through the transparent invocation of a link // this is just a form of syntactic sugar handle.someMethod(some_parameter); // This will call AJAX POST http://some.server/some/scope/some/gadget/someMethod?some_parameter=some_parameter
The makeLink method converts a gadget to a link by concatenating its scope and parent scope.
// Compute the link of a gadget based on its scope and the scope of its parents link = router.makeLink(gadget);
The makeLink method can receive option method parameters to create links to published methods more easily.
// Compute the link of a gadget based on its scope and the scope of its parents and pass parameters in link link = router.makeLink(gadget, "POST", "someMethod", {"parameter_name": value});
The router can be used to connect to remote gadgets. In the not so distant future, two routers on different browsers can be interconected through WebRTC. Until this happens, two routers can be interconnected by COMET or Websocket and some intermediate server.
In this example, Gadget A in a remote host and Gadget B is on local host.
First we publish some method of Gadget A by running the following code on remote host:
// This code is executed on remote host (A) var router_a = €("router"), gadget_a = €("a"); // Publish someMethod of gadget_a router_a.declarePublic(gadget_a, "someMethod?parameter={value}", gadget_a.someMethod); // Publish declareRouted method of router_a so that it can be remotely invoked router_a.declarePublic(router_a, "declareRouted", router_a.declareRouted);
Next we invoke on local host the following code:
// This code is executed on local host (B) var router_b = €("router"), gadget_b = €("b"), link_b = router.makeLink(gadget_b, 'callB'), router_a = router.makeHandle('http://remote.com/router'), link_a = "http://remote.com/a"; router_b.declarePublic(gadget_b, "callB", gadget_b.callB); router_a.declareRouted(link_a, someAMethod, link_b);
What will happen after this is that each time someAMethod is called on Gadget A, Gadget A will invoke link_b which itself will call method callB on Gadget B. In order to achieve this we need to publish declareRouted method of router on A side sode that it can be invoked from B.
RenderJS catalog uses the allDocs API of JIO, including queries.
var catalog = €("catalog"), i = 0, link, catalog_item; // Use JIO syntax catalog.allDocs(filter={}).done(function (catalog_item_list) { for (i = 0; i < catalog_item_list.length; i += 1) { catalog_item = catalog_item_list.length[i]; link = catalog_item['link']; makeHandle(link).done(function (handle) { // does a remote call to a living gadget handle.getInterfaceList().done(function (interface_list) { ... }); }); } }
It is possible for example to embed in an HTML5 an SVG editor gadget which itself embeds a menu gadget, both of which are defined by different sources.
renderJS is an open-source library and is licensed under the LGPL license. More information on LGPL can be found here
Last update: Tuesday, June 12th, 2013