diff --git a/doc/wolf.binding.md b/doc/wolf.binding.md index 6abfd1c..cb81293 100644 --- a/doc/wolf.binding.md +++ b/doc/wolf.binding.md @@ -1,11 +1,61 @@ -# NAVIGATOR +# DATA NAVIGATOR + +Wolf.js expands the root object prototype with the navigate function, this navigate function allow datan path navigation with null controls and data processors execution. + +For examble with this object: + + var data={ + "author":"PMS", + "books":[ + { + "title":"The very first", + "date":"12-12-1866", + "publishedBy":{ + "name":"Ancient publisher" + } + } + ] + } + +is easy to get the name of the publisher of the first book with: + + data.navigate("books/0/publishedBy/name"); + +note the usage of the index in arrays. + +or the title: + + data.navigate("books/0/title"); + +but if the path refer to the next book or the first book does not exists this return undefined, and do not throw any errors. + +## NAVIGATOR PROCESSORS TODO! -# READ +# MODEL TODO! +# READ & BIND + +Using the data navigator rules, any field or text node defined in the html between { and } will refer to this path and the data will be used instead of the text. + +if the binding is done in a HTML attribute and the result is null, undefined or empty string, the attribute will not be drawn on HTML. +if it is a text node the text node will be empty. + +the binding can be mixed with literals: + +
And the winner is {winner}.
+ +or + + + +in this case a null or undefined data will have the same effect as an empty string. + +The reference between the HTML element and the data path is maintaned and handled in the way that any chagne to the model will update automatically the HTML on screen + # WRITE By default wolf framework does not write back the data on form inputs to the model when changes but there is a way to define it using the wolf:write attribute. diff --git a/doc/wolf.fragments & templates.md b/doc/wolf.fragments & templates.md new file mode 100755 index 0000000..bc860a6 --- /dev/null +++ b/doc/wolf.fragments & templates.md @@ -0,0 +1 @@ +TODO! \ No newline at end of file diff --git a/doc/wolf.introduction.md b/doc/wolf.introduction.md new file mode 100755 index 0000000..cdd72e5 --- /dev/null +++ b/doc/wolf.introduction.md @@ -0,0 +1,3 @@ +# WOLF.JS + +TODO! \ No newline at end of file diff --git a/doc/wolf.navigation.md b/doc/wolf.navigation.md new file mode 100755 index 0000000..52300d1 --- /dev/null +++ b/doc/wolf.navigation.md @@ -0,0 +1,56 @@ +# NAVIGATOR + +As the application can be divided into multiple fragments the fragment composition in screen can be automated with paths in the URL hash creating +an effect of paths inside the page. + +For this wolf provide the Navigator object. + +A navigator can be created using: + + wolf.createNavigator(element, "app/navigation.json", (navigator) => { + }); + +The first parameter is the element that will handle the navigation this must be a framework managed element, the application root is recommended refer to [introduction](wolf.introduction.md). +The second is the path of the navigation json containing all the navigation definitnion. +The third is a callback, executed when the navigation is loaded and first navigation raised. + +## NAVIGATION PATH + +The navigator uses a path after the hash symbol in the url to create the ilusion of a filesystem based on path patterns. + +## NAVIGATION DEFINITION JSON + +The navigation file is a json object with the next structure + + { + "":{ + "pattern":"", + "set":{ + // simple definition + "":"", + // extended definition + "":{ + "to":"", + "event":"", + "then":{ + //more elements to map once this fragment is ready, same structure as "set" node. + } + }, + "event":"" + } + } + +· is the id of the navigation, any navigation order will use this id. +· is the path pattern to use for this navigation, can contain data parts, see path structure section below. +· is the id of an element in the HTML that will hold the included html, refer to [introduction](wolf.introduction.md) for details about fragments and inclusion. +· is the path of the html fragment file to include. +· is the name of the event inside the loaded fragment controller that will be executed when this fragment is ready ignoring the status of any sub include. +· is the name of the event inside the first element controller to be executed when all the navigation process is ready. + +Note: The "then" section on a extended definitnion will be processed only when the "to" file to include has been satisfied and all the new DOM ready. + +Note: If any of the event names have a "/" starting character, the event is executed on the application controller instead. + +## PATH STRUCTURE + +TODO! \ No newline at end of file diff --git a/wolf.js b/wolf.js index 80ca912..3fae664 100644 --- a/wolf.js +++ b/wolf.js @@ -55,8 +55,9 @@ * passed prior to the object initialization function. * @param {object} objd object definition * @param {function} [callback] callback to be called once the object has been loaded + * @param {string} url url of instantiated code */ - function instantiate(objd, callback) { + function instantiate(objd, callback, url) { var deps = {}; var constructor; // ==== Process arguments @@ -95,8 +96,13 @@ checkFinish(); } } + function loadDependency(name) { + require(name, checkFactory(name), error => { + console.error("Error loading module '" + url + "', can not load dependency '" + name + "'."); + }); + } for (var i in deps) - require(i, checkFactory(i)); + loadDependency(i); checkFinish(); } @@ -106,18 +112,26 @@ * @param {function} [callback] callback to be called once the object has been loaded * @param {fetcher_callfail} [callfail] callback to be called once the object has been loaded */ - function require(url, callback) { + function require(url, callback, callfail) { if (!url) return; url = new URL(url, document.baseURI).href; var obj = objs[url]; if (!obj) { TOOLS.wGet(url, data => { - var inc = Function(`'use strict';return ${data};\n//# sourceURL=${url}`); + try { + var inc = Function(`'use strict';return ${data};\n//# sourceURL=${url}`); + } catch (e) { + throw new Error("Error in module '" + url + "' parsing file: " + e.message); + } instantiate(inc(), (lobj) => { objs[url] = lobj; callback && callback(lobj); - }); + }, url); + }, (error, stage) => { + if (error && error.status && error.status == 404) + console.error("Error loading module '" + url + "', not found on server."); + callfail && callfail(error, stage) }); } else callback && callback(obj); @@ -830,6 +844,10 @@ template.$controller = template.controller; delete template.controller; } + if (template.forcecontroller) { + template.$forcecontroller = template.forcecontroller == "true"; + delete template.forcecontroller; + } /** * Inserts the element into a parent element (replacing any content) * @param {element} parentElement @@ -842,7 +860,7 @@ parentElement.appendChild(nodes[i]); var ptmpl = parentElement.getTemplate(); if (template.$controller) - if (!ptmpl.$controller || ptmpl.$fragmentedController) { + if (!ptmpl.$controller || ptmpl.$fragmentedController || template.$forcecontroller) { ptmpl.$controller = template.$controller; ptmpl.$fragmentedController = true; } else { @@ -861,6 +879,9 @@ }, controller: { bindable: false + }, + forcecontroller: { + bindable: false } }, @@ -1626,35 +1647,55 @@ throw new Error(`Navigation "${id}" not defined.`) current = { id: id, data: data }; var lh = new K.LoadHandler(() => { - if (navEntry.event) { - var eventName = navEntry.event; - var ctrl; - if (eventName[0] == '/') { - eventName = eventName.substr(1); - ctrl = lh.__elem.getApplicationController(); - } else - ctrl = lh.__elem.getController(); - if (ctrl[eventName]) - ctrl[eventName](lh.__elem, id, data); - } + if (navEntry.event) + callEvent(lh.__elem, navEntry.event); for (var i in nav.onChange) try { nav.onChange[i](id, data); } catch { }; }); - if (navEntry.set) { - for (var k in navEntry.set) { - var dest = navEntry.set[k]; + function callEvent(element, eventName) { + var ctrl; + if (eventName[0] == '/') { + eventName = eventName.substr(1); + ctrl = element.getApplicationController(); + } else + ctrl = element.getController(); + if (ctrl[eventName]) + ctrl[eventName](lh.__elem, id, data); + else + console.error("Navigation event '" + eventName + "' doest not exists"); + } + + function navFragment(dest, elem) { + lh.enter(); + loadFragmentTo(dest.to, elem, (templ, setElem) => { + lh.__elem = lh.__elem || setElem; + if (dest.then) + processSet(dest.then); + if (dest.event) + callEvent(setElem, dest.event); + lh.leave(); + }); + } + + function processSet(navset) { + for (var k in navset) { + var dest = navset[k]; var elem = element.byId(k); - if (elem) { - lh.enter(); - loadFragmentTo(dest, elem, (templ, setElem) => { - lh.__elem = setElem; - lh.leave(); - }); - } + if (typeof dest === "string") + dest = { to: dest }; + if (elem) + navFragment(dest, elem); + else + console.error("Navigation '" + id + "' not completed, element with id '" + k + "' not found") } - } else { lh.clear(); } + } + + if (navEntry.set) + processSet(navEntry.set) + else + lh.clear(); } /** @@ -1914,8 +1955,12 @@ function fail(error, stage) { if (callfail) callfail(error, stage); - else - console.error(error, stage); + else { + if (error && error.status && error.status == 404) + console.error("Error loading '" + url + "', not found"); + else + console.error(error, stage); + } } if (!url) { fail(new Error("Missing url"), "init");