javascript - Recursive builder nesting issues -
i trying parse json / javascript object html... of it's working, can't figure out recursive build method (app.layout.form.documentdirector)...
it keeps on nesting (appending) elements when shouldn't. see example , code @ http://jsfiddle.net/blogshop/dsxft/6/
can me figure out? build method on line 408 (in fiddle -- scroll down documentdirector bit see here). thanks!
build method
build: function (obj, level) { var = this, builder = this.getbuilder(), iterator = object.create(app.utilities.recursiveiterator(obj)), level = level || 0, prev = this._prevlevel || 0, next = level, key, current, attributes, pair, children, hastype = false, maxnesting = false; { // current node , build key = iterator.key(); current = iterator.current(); console.log(iterator.key() + ": " + iterator.current().tosource()); if (current.hasownproperty('type') && current.type !== '') { if (current.hasownproperty('type') && current.type !== '') { //console.log(iterator.current().type); attributes = {}; $.each(current, function(key, value) { if (that._validattributes.indexof(key) !== -1) { attributes[key] = value; } }); //console.log(attributes); } //console.log("prev: " + prev); console.log("level: " + level); if (level == prev && level == 0) { builder.append(current.type, attributes); } else if (level == prev && level > 0) { builder.add(current.type, attributes); } else if (level < prev && level > 0) { builder.parent().append(current.type, attributes); } else if (level > prev && level > 0) { builder.append(current.type, attributes); } else { // nothing } if (current.hasownproperty('label')) { var label = builder.addbefore(builder.getcurrent(), 'label'); builder.text(current.label, label); } } // there child nodes? if so, recurse... if (iterator.haschildren()) { children = iterator.getchildren(); if (this.iscollection(key, current)) { //console.log("i container"); } //console.log("i have children"); console.log("-----------------------------"); hastype = (current.hasownproperty('type') && current.type !== '') ? current.type : false; if (hastype) { maxnesting = (this.isvoid(current.type)) ? true : false; } if (maxnesting === false) { next = level; if (hastype) { this._prevlevel = level; next = level + 1; } this.build(children, next); } } else { if (current.hasownproperty('type') && current.type !== '') { //console.log("i childless"); console.log("-----------------------------"); } } iterator.next(); } while (iterator.hasnext()); if (current && current.hasownproperty('type') && current.type !== '') { if (iterator.hasnext() === false) { //console.log("<----- moving " + parseint(level - prev) + " levels ----->"); //console.log("diff: " + parseint(level - prev)); //console.log("level: " + level); //console.log("prev: " + prev); builder.parent(); this._prevlevel = level - prev; } } }, additional info
documentdirector: function (builder) { var director = object.create({ _builder: {}, _validcollection: ['sections', 'forms', 'fieldsets', 'rows', 'fields'], _validattributes: ['id', 'name'], _voidelements: ['base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'img', 'input', 'link', 'meta', 'param', 'source'], _inputelements: ['text', 'select', 'radio', 'checkbox', 'textarea', 'datepicker', 'yesno'], _prevlevel: 0, init: function (builder) { builder = builder || ''; if (builder) this.setbuilder(builder); return this; }, setbuilder: function (builder) { this._builder = builder; return this; }, getbuilder: function () { return this._builder; }, iscollection: function (key, node) { // collections must have key, don't have type key = key || ''; if (key === '') return false; return (node.constructor === array && this._validcollection.indexof(key) !== -1) ? true : false; }, isvoid: function (type) { var isvoidelement = false; if (this._voidelements.indexof(type.tostring()) !== -1) { isvoidelement = true; } if (this._inputelements.indexof(type.tostring()) !== -1) { isvoidelement = true; } return isvoidelement; }, build: function (obj, level) { var = this, builder = this.getbuilder(), iterator = object.create(app.utilities.recursiveiterator(obj)), level = level || 0, prev = this._prevlevel || 0, next = level, key, current, attributes, pair, children, hastype = false, maxnesting = false; { // current node , build key = iterator.key(); current = iterator.current(); console.log(iterator.key() + ": " + iterator.current().tosource()); if (current.hasownproperty('type') && current.type !== '') { if (current.hasownproperty('type') && current.type !== '') { //console.log(iterator.current().type); attributes = {}; $.each(current, function(key, value) { if (that._validattributes.indexof(key) !== -1) { attributes[key] = value; } }); //console.log(attributes); } //console.log("prev: " + prev); console.log("level: " + level); if (level == prev && level == 0) { builder.append(current.type, attributes); } else if (level == prev && level > 0) { builder.add(current.type, attributes); } else if (level < prev && level > 0) { builder.parent().append(current.type, attributes); } else if (level > prev && level > 0) { builder.append(current.type, attributes); } else { // nothing } if (current.hasownproperty('label')) { var label = builder.addbefore(builder.getcurrent(), 'label'); builder.text(current.label, label); } } // there child nodes? if so, recurse... if (iterator.haschildren()) { children = iterator.getchildren(); if (this.iscollection(key, current)) { //console.log("i container"); } //console.log("i have children"); console.log("-----------------------------"); hastype = (current.hasownproperty('type') && current.type !== '') ? current.type : false; if (hastype) { maxnesting = (this.isvoid(current.type)) ? true : false; } if (maxnesting === false) { next = level; if (hastype) { this._prevlevel = level; next = level + 1; } this.build(children, next); } } else { if (current.hasownproperty('type') && current.type !== '') { //console.log("i childless"); console.log("-----------------------------"); } } iterator.next(); } while (iterator.hasnext()); if (current && current.hasownproperty('type') && current.type !== '') { if (iterator.hasnext() === false) { //console.log("<----- moving " + parseint(level - prev) + " levels ----->"); //console.log("diff: " + parseint(level - prev)); //console.log("level: " + level); //console.log("prev: " + prev); builder.parent(); this._prevlevel = level - prev; } } }, getdocument: function () { return this.getbuilder().getdocument(); } }); return director.init(builder); } dombuilder (injected documentdirector -- handles dom manipulation):
dombuilder: function () { var dombuilder = object.create({ _document: {}, _rootnode: {}, _currentnode: {}, init: function (viewmodel, dombuilder) { var doc, rootnode; this._document = doc = document.createdocumentfragment(); this._rootnode = rootnode = this.appendnode(doc, 'section'); this._currentnode = rootnode; return this; }, /* generic methods ------------------ */ /** * returns document * * @return dom node: dom document fragment */ getdocument: function () { return this._document; }, /** * returns root node * * @return dom node: root node */ getroot: function () { return this._rootnode; }, /** * returns current node * * @return dom node: current node */ getcurrent: function () { return this._currentnode; }, /** * sets current node * * @return dom node: current node */ setcurrent: function (node) { this._currentnode = node; return this; }, /** * returns parent of current node * * @return dom node: parent node */ getparent: function () { return this._currentnode.parentnode; }, /** * creates , appends node inside specified parent * * @ref dom node: insertion target new node * @type string: valid html5 element type * @attributes object: , object containing key-value pairs of attributes , values * * @return dom node: newly created node */ appendnode: function (ref, type, attributes) { var node = document.createelement(type); ref.appendchild(node); //this._currentnode = node; return node; }, /** * creates node , inserts before specified element * * @ref dom node: reference node inserting new node * @type string: valid html5 element type * @attributes object: , object containing key-value pairs of attributes , values * * @return dom node: newly created node */ addbefore: function (ref, type, attributes) { var node = document.createelement(type); ref.parentnode.insertbefore(node, ref); //this._currentnode = node; return node; }, /** * creates node , inserts after specified element * * @parent dom node: reference node inserting new node * @type string: valid html5 element type * @attributes object: , object containing key-value pairs of attributes , values * * @return dom node: newly created node */ addafter: function (ref, type, attributes) { var node = document.createelement(type); ref.parentnode.insertbefore(node, ref.nextsibling); //this._currentnode = node; return node; }, /* chainable methods ---------------------- */ /** * creates , appends node inside specified parent * * @type string: valid html5 element type * @attributes object: , object containing key-value pairs of attributes , values * @ref dom node: (optional) insertion target new node * * @return dombuilder: */ append: function (type, attributes, ref) { var parent, node; ref = ref || this._currentnode; node = document.createelement(type); ref.appendchild(node); this._currentnode = node; if (attributes) { // todo: use map instead $.each(attributes, function (key, value) { node.setattribute(key, value); }); } return this; }, /** * creates node , inserts after specified element * * @type string: valid html5 element type * @attributes object: , object containing key-value pairs of attributes , values * @ref dom node: reference node inserting new node * * @return dombuilder: */ add: function (type, attributes, ref) { var ref, node; ref = ref || this._currentnode; node = document.createelement(type); //console.log(ref); ref.parentnode.insertbefore(node, ref.nextsibling); this._currentnode = node; if (attributes) { // todo: use map instead $.each(attributes, function (key, value) { node.setattribute(key, value); }); } return this; }, /** * creates node , inserts before specified element * * @type string: valid html5 element type * @attributes object: , object containing key-value pairs of attributes , values * @ref dom node: reference node inserting new node * * @return dombuilder: */ before: function (type, attributes, ref) { var ref, node; ref = ref || this._currentnode; node = document.createelement(type); ref.parentnode.insertbefore(node, ref); this._currentnode = node; if (attributes) { // todo: use map instead $.each(attributes, function (key, value) { node.setattribute(key, value); }); } return this; }, /** * sets internal current node reference parent of current node * * @return dombuilder: */ parent: function () { var ref, node; ref = ref || this._currentnode; this._currentnode = this._currentnode.parentnode; return this; }, /** * sets text specified node * * @return dombuilder: */ text: function (value, ref) { var node = document.createtextnode(value); ref.appendchild(node); return this; } }); return dombuilder.init(); } bootstrap:
var formlayout = [ { type: 'section', id: 'header', }, { type: 'div', id: 'center-pane', forms: [ { type: 'section', id: 'claim-history', label: 'add claim history', templates: [ { fieldsets: [ { type: 'fieldset', id: 'claim-history-details', label: 'details', rows: [ { type: 'div', fields: [ { // standard html5 attributes can defined id: 'maincontent_detailscontent_lossdate', name: 'lossdate', label: 'date of loss', type: 'datepicker', // types: standard html5 form element or kendo ui widget classname: 'small', data: { role: 'datepicker', // redundant proxy type parameter bind: { value: 'datepickervalue' // bind kendo ui datepicker } } } ] }, { type: 'div', fields: [ { // standard html5 attributes can defined id: 'maincontent_detailscontent_typeofloss', name: 'typeofloss', label: 'type of loss', type: 'select', // combobox, classname: '' } ] }, { type: 'div', fields: [ { // standard html5 attributes can defined id: 'maincontent_detailscontent_atfaultpercentage', name: 'atfaultpercentage', label: 'at fault %', type: 'text', classname: 'tiny' }, { // standard html5 attributes can defined id: 'maincontent_detailscontent_causeofloss', name: 'causeofloss', label: 'cause of loss', type: 'select', classname: '' } ] }, { type: 'div', fields: [ { // standard html5 attributes can defined id: '', name: '', label: 'charges laid', type: 'yesno', classname: '', data: { bind: { source: 'yesno', value: 'datepickervalue' // bind kendo ui datepicker } } }, { // standard html5 attributes can defined id: 'maincontent_detailscontent_chargeslaid', name: 'chargeslaid', label: 'details', type: 'textarea', classname: 'tiny' } ] } ], }, { type: 'fieldset', id: 'claim-history-amounts', label: 'amounts', rows: [ { type: 'div', fields: [ { // standard html5 attributes can defined id: 'maincontent_detailscontent_seca', name: 'seca', label: 'sec a', type: 'yesno', classname: 'tiny' }, { // standard html5 attributes can defined id: 'maincontent_detailscontent_secatotal', name: 'secatotal', label: 'sec total', type: 'text', classname: 'small' } ] }, { type: 'div', fields: [ { // standard html5 attributes can defined id: 'maincontent_detailscontent_secc', name: 'secc', label: 'sec c', type: 'text', classname: 'tiny' }, ] }, { type: 'div', fields: [ { // standard html5 attributes can defined id: 'maincontent_detailscontent_ignorereason', name: 'ignorereason', label: 'ignore reason', type: 'select', classname: '' } ] } ], }, { type: 'fieldset', id: 'claim-history-vehicle-driver', label: 'vehicle , driver', rows: [ { type: 'div', fields: [ { // standard html5 attributes can defined id: 'maincontent_detailscontent_claimvehicle', name: 'claimvehicle', label: 'claim vehicle', type: 'select', classname: '' } ] } ], } // end fieldset ] // end fieldsets } // end template ] // end templates } // end form ] // end forms }, { type: 'div', id: 'left-pane' }, { type: 'div', id: 'right-pane' } ]; // end sections var formbuilder = object.create(app.layout.form.dombuilder()); var formdirector = object.create(app.layout.form.documentdirector(formbuilder)); formdirector.build(formlayout); document.getelementbyid("test").appendchild(formdirector.getdocument());
solved -- each successive recurse, should have been appending first child node (of object passed in), , adding remaining nodes. added isfirst variable keep track.
working fiddle if anyone's interested... http://jsfiddle.net/blogshop/dsxft/
build: function (obj, level) { var = this, builder = this.getbuilder(), iterator = object.create(app.utilities.recursiveiterator(obj)), level, key, current, attributes, pair, children, isfirst = true, hastype = false, maxnesting = false; { // current node , build key = iterator.key(); current = iterator.current(); hastype = (current.hasownproperty('type') && current.type !== '') ? current.type : false; if (hastype) { maxnesting = (this.isvoid(current.type)) ? true : false; attributes = {}; $.each(current, function(key, value) { if (that._validattributes.indexof(key) !== -1) { attributes[key] = value; } }); if (isfirst == true) { builder.append(current.type, attributes); isfirst = false; } else { builder.add(current.type, attributes) } // prepend label field if (current.hasownproperty('label')) { var label = builder.addbefore(builder.getcurrent(), 'label'); builder.text(current.label, label); } } // there child nodes? if so, recurse... if (iterator.haschildren()) { children = iterator.getchildren(); if (maxnesting === false) { // recurse level = (hastype) ? level + 1 : level; this.build(children, level); } } // move next node iterator.next(); } while (iterator.hasnext()); if (current && current.hasownproperty('type') && current.type !== '') { if (iterator.hasnext() === false) builder.parent(); } },
Comments
Post a Comment