client/README.md
changeset 454 03e8815a012d
parent 440 2311be523b92
child 493 6f2ab1a72f4a
equal deleted inserted replaced
453:04b7d46e9d67 454:03e8815a012d
     7 
     7 
     8 ## Embedding Renkan in a web page
     8 ## Embedding Renkan in a web page
     9 
     9 
    10 Whatever configuration you have, you need to import those javascript files **in that order**...
    10 Whatever configuration you have, you need to import those javascript files **in that order**...
    11 
    11 
    12     <script src="[...]/jquery.min.js"></script>
    12 ```html
    13     <script src="[...]/jquery.mousewheel.min.js"></script>
    13 <script src="[...]/jquery.min.js"></script>
    14     <script src="[...]/underscore-min.js"></script>
    14 <script src="[...]/jquery.mousewheel.min.js"></script>
    15     <script src="[...]/backbone.js"></script>
    15 <script src="[...]/underscore-min.js"></script>
    16     <script src="[...]/backbone-relational.js"></script>
    16 <script src="[...]/backbone.js"></script>
    17     <script src="[...]/paper.js"></script>
    17 <script src="[...]/backbone-relational.js"></script>
    18     <script src="[...]/renkan.js"></script>
    18 <script src="[...]/paper.js"></script>
       
    19 <script src="[...]/renkan.js"></script>
       
    20 ```
    19 
    21 
    20 ... and the renkan css file :
    22 ... and the renkan css file :
    21 
    23 
    22     <link rel="stylesheet" href="[...]/renkan.css" />
    24 ```html
       
    25 <link rel="stylesheet" href="[...]/renkan.css" />
       
    26 ```
    23 
    27 
    24 Finally, add the div in you DOM :
    28 Finally, add the div in you DOM :
    25 
    29 
    26     <div id="renkan"></div>
    30 ```html
       
    31 <div id="renkan"></div>
       
    32 ```
    27 
    33 
    28 Your Renkan should be displayed. Now let's see more specifically the 2 displays possibilities : in body 100% or in a div with set width and height.
    34 Your Renkan should be displayed. Now let's see more specifically the 2 displays possibilities : in body 100% or in a div with set width and height.
    29 
    35 
    30 N.B. : renkan.js is the concatenation of those js files : header.js main.js models.js defaults.js i18n.js paper-renderer.js full-json.js ldtjson-bin.js list-bin.js wikipedia-bin.js.
    36 N.B. : renkan.js is the concatenation of those js files : header.js main.js models.js defaults.js i18n.js paper-renderer.js full-json.js ldtjson-bin.js list-bin.js wikipedia-bin.js.
    31 It is built with the script available in sbin/build/compil.sh.
    37 It is built with the script available in sbin/build/compil.sh.
    32 
    38 
    33 ### In body 100%
    39 ### In body 100%
    34 
    40 
    35 Nothing to add, the html is very simple :
    41 Nothing to add, the html is very simple :
    36 
    42 
    37     <body>
    43 ```html
    38         <div id="renkan"></div>
    44 <body>
    39     </body>
    45     <div id="renkan"></div>
    40 
    46 </body>
       
    47 ```
    41 
    48 
    42 ### In a div
    49 ### In a div
    43 
    50 
    44 The renkan div has the css attributes position:absolute/top:0/left:0/bottom:0/right:0,
    51 The renkan div has the css attributes position:absolute/top:0/left:0/bottom:0/right:0,
    45 so the parent div has to be in position:relative and define width and height. Here is a simple example including css and partial html :
    52 so the parent div has to be in position:relative and define width and height. Here is a simple example including css and partial html :
    46 
    53 
    47     <head>
    54 ```html
    48       <style type="text/css">
    55 <head>
    49         body{
    56   <style type="text/css">
    50             margin: 0 auto;
    57     body{
    51             width: 960px;
    58         margin: 0 auto;
    52         }
    59         width: 960px;
    53         .header, .footer {
    60     }
    54             font-size: 14px;
    61     .header, .footer {
    55             height: 40px;
    62         font-size: 14px;
    56             padding-top: 10px;
    63         height: 40px;
    57         }
    64         padding-top: 10px;
    58         .rnk-container{
    65     }
    59             height: 500px;
    66     .rnk-container{
    60             position: relative;
    67         height: 500px;
    61             width: 700px;
    68         position: relative;
    62         }
    69         width: 700px;
    63       </style>
    70     }
    64     </head>
    71   </style>
    65     <body>
    72 </head>
    66       <div class="header">
    73 <body>
    67         This is a header
    74   <div class="header">
    68       </div>
    75     This is a header
    69       <div class="rnk-container">
    76   </div>
    70          <div id="renkan"></div>
    77   <div class="rnk-container">
    71       </div>
    78      <div id="renkan"></div>
    72       <div class="footer">
    79   </div>
    73         This is a footer
    80   <div class="footer">
    74       </div>
    81     This is a footer
    75     </body>
    82   </div>
    76 
    83 </body>
       
    84 ```
    77 
    85 
    78 ## Embedding a read only Renkan
    86 ## Embedding a read only Renkan
    79 
    87 
    80 To embed a read only Renkan, just add this script tag :
    88 To embed a read only Renkan, just add this script tag :
    81 
    89 
    82     <script type="text/javascript">
    90 ```html
    83         var _renkan;
    91 <script type="text/javascript">
    84         $(function() {
    92     var _renkan;
    85             _renkan = new Rkns.Renkan({
    93     $(function() {
    86                 editor_mode: false,
    94         _renkan = new Rkns.Renkan({
    87                 show_bins: false,
    95             editor_mode: false,
    88             });
    96             show_bins: false,
    89             Rkns.jsonIO(_renkan, {
    97         });
    90                 url: "any_local_or_jsonp_url"
    98         Rkns.jsonIO(_renkan, {
    91             });
    99             url: "any_local_or_jsonp_url"
    92         });
   100         });
    93     </script>
   101     });
    94 
   102 </script>
       
   103 ```
    95 
   104 
    96 
   105 
    97 ## Embedding a writable Renkan
   106 ## Embedding a writable Renkan
    98 
   107 
    99 ### Simple mode : no bins on the left, just the canvas
   108 ### Simple mode : no bins on the left, just the canvas
   100 
   109 
   101 To embed a simple writable Renkan, just add this script tag. In the client folder, "data/simple-persist.php" makes a very simple persistent url.
   110 To embed a simple writable Renkan, just add this script tag. In the client folder, "data/simple-persist.php" makes a very simple persistent url.
   102 The persistent url is supposed to give the json data on GET request at page load, and save datas at PUT request sent by the browser :
   111 The persistent url is supposed to give the json data on GET request at page load, and save datas at PUT request sent by the browser :
   103 
   112 
   104     <script type="text/javascript">
   113 ```html
   105         var _renkan;
   114 <script type="text/javascript">
   106         $(function() {
   115     var _renkan;
   107             _renkan = new Rkns.Renkan({
   116     $(function() {
   108                 show_bins: false
   117         _renkan = new Rkns.Renkan({
   109             });
   118             show_bins: false
   110             Rkns.jsonIO(_renkan, {
   119         });
   111                 url: "url_of_a_persistent_connection"
   120         Rkns.jsonIO(_renkan, {
   112             });
   121             url: "url_of_a_persistent_connection"
   113         });
   122         });
   114     </script>
   123     });
       
   124 </script>
       
   125 ```
   115 
   126 
   116 
   127 
   117 ## Search and bins
   128 ## Search and bins
   118 
   129 
   119 On the right of your renkan interface, you can add some search engine and data bins.
   130 On the right of your renkan interface, you can add some search engine and data bins.
   120 
   131 
   121 Search engine can be the current [IRI's Lignes de temps platform](http://ldt.iri.centrepompidou.fr/) and wikipedia in any available language.
   132 Search engine can be the current [IRI's Lignes de temps platform](http://ldt.iri.centrepompidou.fr/) and wikipedia in any available language.
   122 Here is an example of configuration :
   133 Here is an example of configuration :
   123 
   134 
   124     _renkan = new Rkns.Renkan({
   135 ```js
   125         search: [
   136 _renkan = new Rkns.Renkan({
   126                 {
   137     search: [
   127                     type: "Ldt"
   138             {
   128                 },
   139                 type: "Ldt"
   129                 {
   140             },
   130                     type: "Wikipedia",
   141             {
   131                     lang: "fr"
   142                 type: "Wikipedia",
   132                 },
   143                 lang: "fr"
   133                 {
   144             },
   134                     type: "Wikipedia",
   145             {
   135                     lang: "ja"
   146                 type: "Wikipedia",
   136                 }
   147                 lang: "ja"
   137         ]
   148             }
   138     });
   149     ]
   139     Rkns.jsonIO(_renkan, {
   150 });
   140         url: "data/simple-persist.php"
   151 Rkns.jsonIO(_renkan, {
   141     });
   152     url: "data/simple-persist.php"
       
   153 });
       
   154 ```
   142 
   155 
   143 You can also define data bins : annotations loaded from IRI's Lignes de temps projects, and any resources which can be drag and dropped into the renkan.
   156 You can also define data bins : annotations loaded from IRI's Lignes de temps projects, and any resources which can be drag and dropped into the renkan.
   144 Resources can be simple texts, links or objects with title, description, url and image. Here is an example of configuration :
   157 Resources can be simple texts, links or objects with title, description, url and image. Here is an example of configuration :
   145 
   158 
   146     _renkan = new Rkns.Renkan({
   159 ```js
   147         search: [
   160 _renkan = new Rkns.Renkan({
   148             ...
   161     search: [
   149         ],
   162         ...
   150         bins: [
   163     ],
   151             {
   164     bins: [
   152                 title: "To be or not to be on Lignes de Temps",
   165         {
   153                 type: "Ldt",
   166             title: "To be or not to be on Lignes de Temps",
   154                 ldt_type: "Project",
   167             type: "Ldt",
   155                 project_id: "6af4019c-8283-11e2-9678-00145ea4a2be",
   168             ldt_type: "Project",
   156                 ldt_platform: "http://ldt.iri.centrepompidou.fr/"
   169             project_id: "6af4019c-8283-11e2-9678-00145ea4a2be",
   157             },
   170             ldt_platform: "http://ldt.iri.centrepompidou.fr/"
   158             {
   171         },
   159                 type: "ResourceList",
   172         {
   160                 title: "Ressources",
   173             type: "ResourceList",
   161                 list: [
   174             title: "Ressources",
   162                     {
   175             list: [
   163                         url: "http://www.google.com/",
   176                 {
   164                         title: "Google",
   177                     url: "http://www.google.com/",
   165                         description: "Search engine",
   178                     title: "Google",
   166                         image: "http://www.google.fr/images/srpr/logo4w.png"
   179                     description: "Search engine",
   167                     },
   180                     image: "http://www.google.fr/images/srpr/logo4w.png"
   168                     "Polemic Tweet http://www.polemictweet.com",
   181                 },
   169                     "Twitter http://www.twitter.com/"
   182                 "Polemic Tweet http://www.polemictweet.com",
   170                 ]
   183                 "Twitter http://www.twitter.com/"
   171             }
   184             ]
   172         ]
   185         }
   173     });
   186     ]
   174     Rkns.jsonIO(_renkan, {
   187 });
   175         url: "data/simple-persist.php"
   188 Rkns.jsonIO(_renkan, {
   176     });
   189     url: "data/simple-persist.php"
       
   190 });
       
   191 ```
       
   192 
   177 
   193 
   178 If you embed the renkan in a div, the renkan container css should have overflow:hidden in order to hide bins when the user wants to.
   194 If you embed the renkan in a div, the renkan container css should have overflow:hidden in order to hide bins when the user wants to.
   179 
   195 
   180 ## More configuration
   196 ## More configuration
   181 
   197 
   182 You can configure several things :
   198 You can configure several things :
   183 * the language of your interface, english (default) or french
   199 * the language of your interface, english (default) or french
   184 * you can fill your nodes with black color instead of transparent.
   200 * you can fill your nodes with black color instead of transparent.
   185 * thanks to an external file, you can define properties for links between node.
   201 * thanks to an external file, you can define properties for links between node.
   186 
   202 
   187 
   203 ```js
   188     _renkan = new Rkns.Renkan({
   204 _renkan = new Rkns.Renkan({
   189         ...
   205     ...
   190         property_files: [ "data/properties.json" ],
   206     property_files: [ "data/properties.json" ],
   191         node_fill_color: true,
   207     node_fill_color: true,
   192         language: "fr"
   208     language: "fr"
       
   209 });
       
   210 ```
       
   211 
       
   212 Here is an example of properties file :
       
   213 
       
   214 ```json
       
   215 [
       
   216     {
       
   217         "label": "Dublin Core Metadata",
       
   218         "base-uri": "http://purl.org/dc/elements/1.1/",
       
   219         "properties": [
       
   220             {
       
   221                 "uri": "creator",
       
   222                 "label": "created by"
       
   223             }, {
       
   224                 "uri": "date",
       
   225                 "label": "has date"
       
   226             }, {
       
   227                 "uri": "subject",
       
   228                 "label": "has subject"
       
   229             }
       
   230         ]
       
   231     }, {
       
   232         "label": "SKOS Semantic relations",
       
   233         "base-uri": "http://www.w3.org/2004/02/skos/core#",
       
   234         "properties": [
       
   235             {
       
   236                 "uri": "broader",
       
   237                 "label": "has broader"
       
   238             }, {
       
   239                 "uri": "narrower",
       
   240                 "label": "has narrower"
       
   241             }, {
       
   242                 "uri": "related",
       
   243                 "label": "has related"
       
   244             }
       
   245         ]
       
   246     }
       
   247 ]
       
   248 ```
       
   249 
       
   250 
       
   251 Finally, here is an example of full configuration :
       
   252 
       
   253 ```html
       
   254 <script type="text/javascript">
       
   255     var _renkan;
       
   256     $(function() {
       
   257         _renkan = new Rkns.Renkan({
       
   258             search: [
       
   259                 {
       
   260                     type: "Ldt"
       
   261                 },
       
   262                 {
       
   263                     type: "Wikipedia",
       
   264                     lang: "fr"
       
   265                 },
       
   266                 {
       
   267                     type: "Wikipedia",
       
   268                     lang: "ja"
       
   269                 }
       
   270             ],
       
   271             bins: [
       
   272                 {
       
   273                     title: "Projet Lignes de Temps",
       
   274                     type: "Ldt",
       
   275                     ldt_type: "Project",
       
   276                     project_id: "6af4019c-8283-11e2-9678-00145ea4a2be",
       
   277                     ldt_platform: "http://ldt.iri.centrepompidou.fr/"
       
   278                },
       
   279                 {
       
   280                     type: "ResourceList",
       
   281                     title: "Ressources",
       
   282                     list: [
       
   283                         {
       
   284                             url: "http://www.google.com/",
       
   285                             title: "Google",
       
   286                             description: "Search engine",
       
   287                             image: "http://www.google.fr/images/srpr/logo4w.png"
       
   288                         },
       
   289                         "Polemic Tweet http://www.polemictweet.com",
       
   290                         "Twitter http://www.twitter.com/"
       
   291                     ]
       
   292                 }
       
   293             ],
       
   294             property_files: [ "data/properties.json" ],
       
   295             node_fill_color: false,
       
   296             language: "fr"
       
   297         });
       
   298         Rkns.jsonIO(_renkan, {
       
   299             url: "data/simple-persist.php"
       
   300         });
   193     });
   301     });
   194 
   302 </script>
   195 Here is an example of properties file :
   303 ```
   196 
       
   197     [
       
   198         {
       
   199             "label": "Dublin Core Metadata",
       
   200             "base-uri": "http://purl.org/dc/elements/1.1/",
       
   201             "properties": [
       
   202                 {
       
   203                     "uri": "creator",
       
   204                     "label": "created by"
       
   205                 }, {
       
   206                     "uri": "date",
       
   207                     "label": "has date"
       
   208                 }, {
       
   209                     "uri": "subject",
       
   210                     "label": "has subject"
       
   211                 }
       
   212             ]
       
   213         }, {
       
   214             "label": "SKOS Semantic relations",
       
   215             "base-uri": "http://www.w3.org/2004/02/skos/core#",
       
   216             "properties": [
       
   217                 {
       
   218                     "uri": "broader",
       
   219                     "label": "has broader"
       
   220                 }, {
       
   221                     "uri": "narrower",
       
   222                     "label": "has narrower"
       
   223                 }, {
       
   224                     "uri": "related",
       
   225                     "label": "has related"
       
   226                 }
       
   227             ]
       
   228         }
       
   229     ]
       
   230 
       
   231 
       
   232 
       
   233 Finally, here is an example of full configuration :
       
   234 
       
   235     <script type="text/javascript">
       
   236         var _renkan;
       
   237         $(function() {
       
   238             _renkan = new Rkns.Renkan({
       
   239                 search: [
       
   240                     {
       
   241                         type: "Ldt"
       
   242                     },
       
   243                     {
       
   244                         type: "Wikipedia",
       
   245                         lang: "fr"
       
   246                     },
       
   247                     {
       
   248                         type: "Wikipedia",
       
   249                         lang: "ja"
       
   250                     }
       
   251                 ],
       
   252                 bins: [
       
   253                     {
       
   254                         title: "Projet Lignes de Temps",
       
   255                         type: "Ldt",
       
   256                         ldt_type: "Project",
       
   257                         project_id: "6af4019c-8283-11e2-9678-00145ea4a2be",
       
   258                         ldt_platform: "http://ldt.iri.centrepompidou.fr/"
       
   259                    },
       
   260                     {
       
   261                         type: "ResourceList",
       
   262                         title: "Ressources",
       
   263                         list: [
       
   264                             {
       
   265                                 url: "http://www.google.com/",
       
   266                                 title: "Google",
       
   267                                 description: "Search engine",
       
   268                                 image: "http://www.google.fr/images/srpr/logo4w.png"
       
   269                             },
       
   270                             "Polemic Tweet http://www.polemictweet.com",
       
   271                             "Twitter http://www.twitter.com/"
       
   272                         ]
       
   273                     }
       
   274                 ],
       
   275                 property_files: [ "data/properties.json" ],
       
   276                 node_fill_color: false,
       
   277                 language: "fr"
       
   278             });
       
   279             Rkns.jsonIO(_renkan, {
       
   280                 url: "data/simple-persist.php"
       
   281             });
       
   282         });
       
   283     </script>
       
   284 
       
   285 
   304 
   286 ## Drop management
   305 ## Drop management
   287 
   306 
   288 You can override, partially or totally, the function that handle the drop event from an other web page.
   307 You can override, partially or totally, the function that handle the drop event from an other web page.
   289 The current function catches drops from google results page, tweets, links, images and other things like svg paths.
   308 The current function catches drops from google results page, tweets, links, images and other things like svg paths.
   290 If you want to override totally the handler function, you can define a **drop\_handler** function that receives a \_data object
   309 If you want to override totally the handler function, you can define a **drop\_handler** function that receives a \_data object
   291 and returns a node object. A node object has title, description, image and uri properties. The \_data object is received from the
   310 and returns a node object. A node object has title, description, image and uri properties. The \_data object is received from the
   292 browser's drop event. Here is an example of drop\_handler function :
   311 browser's drop event. Here is an example of drop\_handler function :
   293 
   312 
   294     _renkan = new Rkns.Renkan({
   313 ```js
   295         ...
   314 _renkan = new Rkns.Renkan({
   296         drop_handler: function(_data){
   315     ...
   297             var newNode = {};
   316     drop_handler: function(_data){
   298             newNode.title = "Overridden title";
   317         var newNode = {};
   299             newNode.description = "Overridden description " + _data["text/plain"];
   318         newNode.title = "Overridden title";
   300             return newNode;
   319         newNode.description = "Overridden description " + _data["text/plain"];
   301         }
   320         return newNode;
   302     });
   321     }
   303 
   322 });
       
   323 ```
   304 
   324 
   305 You can also define a **drop\_enhancer** function that receives the already formed node object and \_data object. This function has to
   325 You can also define a **drop\_enhancer** function that receives the already formed node object and \_data object. This function has to
   306 return the overriden node object. Here is an example of drop\_enhancer function :
   326 return the overriden node object. Here is an example of drop\_enhancer function :
   307 
   327 
   308     _renkan = new Rkns.Renkan({
   328 ```js
       
   329 _renkan = new Rkns.Renkan({
       
   330     ...
       
   331     drop_enhancer: function(newNode, _data){
       
   332         newNode.title = "Prefixed title : " + newNode.title;
       
   333         return newNode;
       
   334     }
       
   335 });
       
   336 ```
       
   337 
       
   338 ## Data and api
       
   339 
       
   340 ### Data
       
   341 
       
   342 The data exchanged by Renkan is a json object ths following structure:
       
   343 (Warning: some fields are optionals, and )
       
   344 
       
   345 ```json
       
   346 {
       
   347     "id": "f4d002b7-d4fd-486c-8898-6c6ceebc3354",
       
   348     "title": "Example of Renkan with movies",
       
   349     "description": "A long description",
       
   350     "created": "2013-03-18T11:32:40.253+01:00",
       
   351     "updated": "2014-02-04T15:12:56.619+01:00",
       
   352     "nodes": [
       
   353         {
       
   354             "id": "node-2013-05-08-72c911bafdf9932c-0001",
       
   355             "title": "Une femme mène l'enquête",
       
   356             "description": "La caméra suit la femme qui marche\nJeu avec la caméra qui se substitue au spectateur",
       
   357             "uri": "http://ldt.iri.centrepompidou.fr/ldtplatform/ldt/front/player/lyceehulst_3extraits/c8a61ee4-b33c-11e2-802c-00145ea4a2be#id=s_DCA8D184-EFC2-314B-0F6B-84043E8F9984",
       
   358             "color": null,
       
   359             "position": {
       
   360                 "x": -547.0499881440252,
       
   361                 "y": -221.5401229374163
       
   362             },
       
   363             "image": "http://ldt.iri.centrepompidou.fr/static/site/ldt/css/imgs/video_sequence.png",
       
   364             "size": 0,
       
   365             "project_id": "f4d002b7-d4fd-486c-8898-6c6ceebc3354",
       
   366             "created_by": "de68xf75y6hs5rgjhgghxbm217xk",
       
   367             "shape": "circle",
       
   368             "type": "...",
       
   369             "hidden": false
       
   370         },
   309         ...
   371         ...
   310         drop_enhancer: function(newNode, _data){
   372     ],
   311             newNode.title = "Prefixed title : " + newNode.title;
   373     "edges": [
   312             return newNode;
   374         {
       
   375             "id": "edge-2013-05-08-72c911bafdf9932c-0002",
       
   376             "title": "",
       
   377             "description": "",
       
   378             "uri": "",
       
   379             "color": "#ff7f00",
       
   380             "from": "node-2013-04-30-a81adec6694db5f4-0032",
       
   381             "to": "node-2013-05-08-72c911bafdf9932c-0001",
       
   382             "project_id": "f4d002b7-d4fd-486c-8898-6c6ceebc3354",
       
   383             "created_by": "de68xf75y6hs5rgjhgghxbm217xk"
       
   384         },
       
   385         ...
       
   386     ],
       
   387     "users": [ #optional
       
   388         {
       
   389             "userId": "user-2015-05-05-72c911bafdf9932c-0001",
       
   390             "color": "#cc9866",
       
   391             "username": "user1",
       
   392             "anonymous": true
       
   393         },
       
   394     ...
       
   395     ],
       
   396     "space_id": "17f968e4-2640-4319-aa61-b5b8b527ebb4", #Optional
       
   397     "views": [ #Optional
       
   398         {
       
   399             "zoom_level": 0.8275032552816195,
       
   400             "offset_x": 832.0104075533723,
       
   401             "offset_y": 402.8917139487223
   313         }
   402         }
   314     });
   403     ]
   315 
   404 }
   316 ## Dev
   405 ```
       
   406 
       
   407 This data is a direct json serialisation of the data model (cf `client/js/`).
       
   408 
       
   409 
       
   410 ### IO module and server communications
       
   411 
       
   412 Currently 2 modules are defined to load and save data:
       
   413   - save-once: data is loaded by ajax at the page initialization and saved each time the save button is clicked (floppy disc icon)
       
   414   - full-json: data is loaded by ajax at the page initialization and saved each time the data is modified.
       
   415 
       
   416 The code of these 2 modules is rather simple and easy to adapt. the various examples in the `test` folder show how to configure them.
       
   417 Both these 2 modules communicate with the server with a single endpoint. The communication is made with JSON documents and is bidirectional: 'GET' to load the project, 'POST' to send back the modified project.
       
   418 the dev environement (see [dev](#dev)) offer a simple implementation of such an endpoint for testing support. The code of this endpoint is in the file `client/data/simple-persist.js`.
       
   419 
       
   420 
       
   421 ## <a name="dev"></a>Dev
       
   422 
       
   423 Renkan offers a dev environment that uses [Grunt](http://gruntjs.com/) for running tasks and [Bower](http://bower.io/) for managing dependencies.
       
   424 
       
   425 ### Setup
       
   426 
       
   427 The only prerequisite is to install [nodejs](https://nodejs.org/) or [iojs](https://iojs.org/en/index.html). Once installed, the complete dev environment can be obtained with the following commands:
       
   428 
       
   429 ```sh
       
   430 $ npm i
       
   431 $ ./node_modules/.bin/bower install
       
   432 ```
       
   433 
       
   434 
       
   435 ### Grunt tasks
   317 
   436 
   318 We offer a environment to develop without having to build all the project manually after each change. We define few grunt task to help us
   437 We offer a environment to develop without having to build all the project manually after each change. We define few grunt task to help us
   319 making it easier. You can launch them by running './node_modules/.bin/grunt task'.
   438 making it easier. You can launch them by running `./node_modules/.bin/grunt <task>`.
   320 * 'dev': will build th project but let the temporary file like templates.js to let us test the application through the test files
   439 
   321 (see 'Tests' part below). Then it will launch a small testing server and watch the modifications of the js/css/html to build the project
   440 * `dev`: will build the project but keep the temporary file like templates.js to let us test the application through the test files (see ['Tests'](#tests) part below). Then it will launch a small web server for testing and watch the modifications of the js/css/html to build the project again in case of any change. The test server runs on port 9001 on your local machine i.e. http://localhost:9001 . The folder served is the `client/` folder.
   322 again in case of any change.
   441 
   323  
       
   324 Tasks for production :
   442 Tasks for production :
   325 * 'default': will build the project and clean the temporary files (like templates.js) used during development and building.
   443 * `default`: will build the project and clean the temporary files (like templates.js) used during development and building.
   326 * 'copy-server': will copy the built project to the server side.
   444 * `copy-server`: will copy the built project ut put to the server part of this project.
   327  
   445 
   328 ## Tests
   446 
       
   447 ## <a name="tests"></a>Tests
   329 
   448 
   330 Because of a simple php file enabling persistent connection, you can not test the writables examples by only opening them in your browser.
   449 Because of a simple php file enabling persistent connection, you can not test the writables examples by only opening them in your browser.
   331 A url like file:///path/to/folder/test-writable-\*.html will not work.
   450 A url like file:///path/to/folder/test-writable-\*.html will not work.
   332 You need to run a localhost server that executes php and go to a url like http://localhost/renkan/test/test-writable-\*.html.
   451 You need to run a localhost server that executes php and go to a url like http://localhost:9001/test/test-writable-\*.html. (cf. [Dev](#dev) above)
   333 
   452 
   334 For read only examples, it appears that for security reasons urls like file:///path/to/folder/test-readonly-\*.html only work on Firefox.
   453 For read only examples, it appears that for security reasons urls like file:///path/to/folder/test-readonly-\*.html only work on Firefox.
   335 To see those examples with other browsers, you also need to run a localhost server and go to a url like http://localhost/renkan/test/test-readonly-\*.html.
   454 To see those examples with other browsers, you also need to run a localhost server and go to a url like http://localhost:9001/test/test-readonly-\*.html. (cf. [Dev](#dev) above)