// var shadowValue = {}; var isNumeric = function( v ) { return ( !isNaN( parseFloat( v ) ) && isFinite( v ) ); }; var silkMutations = xb.core.object.extend( { ctor: function( node ) { this.node = node; this.logList = []; this.domNodes = []; this.mtime = null; }, isEditable: function( domNode ) { return ( ( typeof( domNode.getAttribute ) === "function" ) && ( domNode.getAttribute( "data-simply-field" ) !== null || domNode.getAttribute( "data-simply-list" ) !== null || domNode.getAttribute( "data-simply-list-item" ) !== null ) ); }, log: function( mutations ) { this.mtime = ( new Date() ).getTime(); /* FIXME: maak een mutation list */ for ( var i = 0, il = mutations.length; i < il; i++ ) { var m = mutations[ i ]; //if ( this.isEditable( m.target ) ) { var p = this.domNodes.indexOf( m.target ); if ( p < 0 ) { p = this.domNodes.length; this.domNodes[ p ] = m.target; } this.logList.push( { domNode: p, mutation: m } ); //} } return this; } } ); var silkMutationObserver = xb.core.object.extend( { defaultProperties: { childList: true, attributes: true, characterData: true, subtree: true, attributeFilter: [ "src", "href", "target", "rel" ] }, defaultMutationDelay: 250, defaultInterval: 125, ctor: function( node, properties ) { var self = this; this.running = []; this.node = node; this.properties = this.defaultProperties; if ( typeof( properties ) === "object" ) { this.properties = xb.core.object.prototype.copy.call( this.defaultProperties ); var keys = Object.getOwnPropertyNames( properties ); for ( var i = 0, il = keys.length; i < il; i++ ) { this.properties[ keys[ i ] ] = properties[ keys[ i ] ]; } } this.mutations = null; this.mutationDelay = this.defaultMutationDelay; this.interval = this.defaultInterval; this.observer = new MutationObserver( function( mutations ) { if ( document.body.getAttribute( "data-simply-edit" ) !== null ) { self.onMutation( mutations ); } } ); this.__watchMethod = function( evt ) { self.watchMethod( false, evt ); }; this.__intervalListener = null; }, onMutation: function( mutations ) { // this.mutations.log( mutations ); // FIXME: Code hieronder is om te voorkomen dat simply-edit // de net toegevoegde domNodes weer voor onze neus weghaalt. // console.log( mutations ); if ( mutations.length && mutations[ 0 ].addedNodes.length ) { //console.log( mutations[ 0 ].addedNodes ); if ( mutations[ 0 ].addedNodes.length == 1 && mutations[ 0 ].addedNodes[ 0 ].nodeName == "SPAN" ) { // console.warn( "ignoring simply-edit span insert?" ); // console.warn( "fixme: check of ie ook direct weer weggehaald wordt" ); return; } this.mutations.mtime = ( new Date() ).getTime(); var _delay = this.mutationDelay; this.mutationDelay = 0; this.watchMethod( false, null ); this.mutationDelay = _delay; } else { this.mutations.mtime = ( new Date() ).getTime(); } }, watchMethod: function( flush, evt ) { if ( this.mutations.mtime !== null ) { var dt = ( new Date() ).getTime() - this.mutations.mtime; if ( dt >= this.mutationDelay ) { if ( typeof( this.node.onDOMChange ) === "function" ) { var node = this.node; var mutations = this.mutations; if ( !flush ) this.stop(); //console.log( "DT", dt, this.mutations ); if ( flush ) { console.warn( "Flushing", mutations ); } this.node.onDOMChange.call( node, mutations, dt, this ); if ( !flush ) this.start(); } this.mutations = new silkMutations( this.node ); } } }, start: function() { if ( this.running.length < 1 ) { // console.log( "Start watching" ); var self = this; var timestamp = ( new Date() ).getTime(); this.mutations = new silkMutations( this.node ); this.observer.observe( this.node.domNode, this.properties ); this.__intervalListener = window.setInterval( this.__watchMethod, this.interval ); } this.running.push( 1 ); return this; }, stop: function() { this.running.pop(); if ( this.running.length < 1 ) { // console.log( "Stop watching" ); this.observer.disconnect(); window.clearInterval( this.__intervalListener ); this.__intervalListener = null; //this.watchMethod( true, null ); } return this; } } ); silk.node = xb.core.object.extend( { factory: function( domNode ) { var mount = domNode.getAttribute( "data-simply-data" ); var list = domNode.getAttribute( "data-simply-list" ); var listItem = domNode.getAttribute( "data-simply-list-item" ); var field = domNode.getAttribute( "data-simply-field" ); var constant = domNode.getAttribute( "data-silk-constant" ); var isConstant = ( constant === null ) ? false : true; if ( isConstant ) { field = constant; } // FIXME: Mount mag niet automatisch gespawned worden, ik wel namelijk // normale objecten? if ( 0 && mount !== null ) { return new silk.node.mount( domNode, list ); } else if ( listItem !== null ) { var type = domNode.getAttribute( "data-silk-type" ); if ( type !== null ) { var o = window; var t = type.split( "." ); for ( var i = 0, il = t.length; i < il; i++ ) { o = o[ t[ i ] ]; if ( typeof( o ) === "undefined" ) { console.error( "silk.node::factory>", type, "not found!" ); break; } } if ( typeof( o ) === "function" ) { return new o( domNode ); } else { console.error( "silk.node::factory>", type, "is not a function!" ); } } return new silk.node.struct( domNode ); } else if ( field !== null ) { return silk.node.field( domNode, field, isConstant ); } else if ( list !== null ) { return new silk.node.list( domNode, list ); } else { console.error( "Node", domNode, "is not a `simply` node" ); return new silk.node.struct( domNode ); } }, ctor: function( domNode ) { this.domNode = domNode; this.parent = null; }, setParent: function( node ) { this.parent = node; return this; }, getParentDOMNodeIn: function( stack ) { var parentElm = this.domNode.parentElement; while ( parentElm !== null ) { for ( var i = stack.length - 1; i >= 0; i-- ) { if ( stack[ i ].domNode === parentElm ) { return i; } } parentElm = parentElm.parentElement; } return null; }, parseDOM: function( node ) { var selector = "[data-simply-list], [data-simply-field], [data-simply-list-item], [data-silk-constant]"; var stack = []; if ( node instanceof silk.node ) { stack = [ node ]; } else if ( this instanceof silk.node.struct ) { stack = [ new silk.node.struct( this.domNode ) ]; } else { stack = [ new silk.node.list( this.domNode ) ]; } $( this.domNode ).find( selector ).each( function() { var node = silk.node( this ); var sp = node.getParentDOMNodeIn( stack ); if ( sp < ( stack.length - 1 ) ) { //console.log( "resetting stack to", sp ); stack = stack.slice( 0, sp + 1 ); } //console.log( "adding", node, "to", sp ); stack[ sp ].add( node ); if ( ( node instanceof silk.node.list ) || ( node instanceof silk.node.struct ) ) { stack.push( node ); } } ); return stack[ 0 ]; } } ); silk.node.struct = xb.core.object.extend( silk.node, { ctor: function( domNode ) { silk.node.prototype.ctor.call( this, domNode ); this.childNodes = {}; this.template = domNode.getAttribute( "data-simply-template" ); }, add: function( node ) { this.childNodes[ node.name ] = node.setParent( this ); return this; }, get: function( offset ) { var node = this.childNodes[ offset ]; if ( node instanceof silk.node ) { return node; } return null; }, getValue: function( options ) { var result = {}; for ( var name in this.childNodes ) { result[ name ] = this.childNodes[ name ].getValue( options ); } var tmpl = this.domNode.getAttribute( "data-simply-template" ); if ( tmpl ) { result[ "data-simply-template" ] = tmpl; } return result; }, setValue: function( data ) { var result = {}; for ( var name in this.childNodes ) { this.childNodes[ name ].setValue( data[ name ] ); } }, each: function( func, args ) { var result = []; if ( typeof( func ) === "function" ) { for ( var name in this.childNodes ) { var r = func.call( this.childNodes[ name ], name, args ); if ( typeof( r ) !== "undefined" && r !== null ) { result.push( r ); } } } return result; }, } ); silk.node.field = xb.core.object.extend( silk.node, { factory: function( domNode, name, isConstant ) { var mapping = { img: [ "src" ], a: [ "href", "target", "rel" ] }; var attribs = mapping[ domNode.tagName.toLowerCase() ]; if ( typeof( attribs ) !== "undefined" ) { return new silk.node.field.attribs( domNode, name, attribs, isConstant ); } return new silk.node.field( domNode, name, isConstant ); }, ctor: function( domNode, name, isConstant ) { silk.node.prototype.ctor.call( this, domNode ); this.name = name; this.constant = ( typeof( isConstant ) !== "boolean" ) ? false : isConstant; }, getValue: function( options ) { if ( options === true ) { return this.domNode.innerText; } return this.domNode.innerHTML; }, setValue: function( data ) { var data = ( ( typeof( data ) !== "undefined" ) ? data : "" ); this.domNode.innerHTML = data; } } ); silk.node.field.attribs = xb.core.object.extend( silk.node.field, { ctor: function( domNode, name, attribs, isConstant ) { silk.node.field.prototype.ctor.call( this, domNode, name, isConstant ); this.attribs = ( attribs instanceof Array ) ? attribs : []; }, getValue: function( options ) { var result = {}; for ( var i = 0, il = this.attribs.length; i < il; i++ ) { var name = this.attribs[ i ]; result[ name ] = this.domNode.getAttribute( name ); } result.innerHTML = silk.node.field.prototype.getValue.call( this, options ); return result; }, setValue: function( data ) { silk.node.field.prototype.setValue.call( this, data.innerHTML ); for ( var i = 0, il = this.attribs.length; i < il; i++ ) { var name = this.attribs[ i ]; this.domNode.setAttribute( name, data[ name ] ); } } } ); silk.node.list = xb.core.object.extend( silk.node.field, { ctor: function( domNode, name ) { silk.node.field.prototype.ctor.call( this, domNode, name ); this.childNodes = []; }, add: function( node ) { this.childNodes.push( node.setParent( this ) ); return this; }, getValue: function( options ) { var result = []; for ( var i = 0, il = this.childNodes.length; i < il; i++ ) { result[ i ] = this.childNodes[ i ].getValue( options ); } return result; }, setValue: function( data, reset ) { silk.html.render( this.domNode, data, reset ); var evt = new CustomEvent( "events/dispatcher/render", { detail: { data: data, object: this, reset: reset } } ); this.domNode.dispatchEvent( evt ); }, each: function( func, args ) { var result = []; if ( typeof( func ) === "function" ) { for ( var i = 0, il = this.childNodes.length; i < il; i++ ) { var r = func.call( this.childNodes[ i ], i, args ); if ( typeof( r ) !== "undefined" && r !== null ) { result.push( r ); } } } return result; }, find: function( cmp, context, offset ) { var result = []; var f = function( context, offset ) { //console.log( "=> checking", offset ); if ( cmp.call( this, context, offset ) === true ) { result.push( this ); } if ( typeof( this.each ) === "function" ) { this.each( f ); } }; this.each( f ); return result; } } ); silk.node.mount = xb.core.object.extend( silk.node.list, { DOMNodeHandler: null, ctor: function( domNode, name, resource ) { silk.node.prototype.ctor.call( this, null ); if ( this.DOMNodeHandler === null ) { this.DOMNodeHandler = silk.node.mount.DOMNode; } this.name = name; if ( !this.path ) { this.path = domNode.getAttribute( "data-simply-path" ); } if ( !this.fieldName ) { this.fieldName = domNode.getAttribute( "data-simply-list" ); } this.nodes = []; this.setResource( resource ); this.addDOMNode( domNode ); }, addDOMNode: function( domNode ) { for ( var i = 0, il = this.nodes.length; i < il; i++ ) { var node = this.nodes[ i ]; if ( node.domNode === domNode ) { return this; } } var node = this.DOMNodeHandler( this, domNode ); // new silk.node.mount.DOMNode( this, domNode ); this.nodes.push( node ); return this; }, getDOMNodes: function() { var result = []; for ( var i = 0, il = this.nodes.length; i < il; i++ ) { result.push( this.nodes[ i ].domNode ); } return result; }, onLoad: function() { console.warn( "silk.node.mount::onLoad", this.name, "still a stub" ); this.display(); }, setResource: function( resource ) { this.resource = ( ( resource instanceof silk.resource ) ? resource : null ); return this; }, getData: function() { return this.resource.data[ this.fieldName ]; }, getResource: function( name ) { return this.resource.getResource( name ); }, getMount: function( name, resourceName ) { if ( arguments.length > 0 ) { return this.resource.getMount.apply( this.resource, arguments ); } return this; }, display: function() { // var data = this.resource.data[ this.fieldName ]; // this.render( data, true ); this.render( null, true ); }, render: function( data, reset ) { for ( var i = 0, il = this.nodes.length; i < il; i++ ) { if ( data === null ) { var name = this.nodes[ i ].domNode.getAttribute( "data-simply-list" ); var nodeData = this.resource.data[ name ]; if ( typeof( nodeData ) === "undefined" ) { nodeData = []; } this.nodes[ i ].display( nodeData, reset ); } else { this.nodes[ i ].display( data, reset ); } } return this; }, onDOMChange: function( handler ) { console.warn( "Mount(", this.name, ")::onDOMChange => no custom implementation", handler ); }, save: function( data ) { return data; } } ); silk.node.mount.DOMNode = xb.core.object.extend( silk.node.list, { factory: function( mount, domNode ) { return new silk.node.mount.DOMNode( mount, domNode ); }, ctor: function( mount, domNode ) { silk.node.list.prototype.ctor.call( this, domNode, mount.name ); this.mount = mount; this.childNodes = this.parseDOM().childNodes; this.mObserver = silkMutationObserver( this ); this.mObserver.start(); console.log( "observing", this.domNode, this.childNodes ); }, onDOMChange: function( mutations, dt, mObserver ) { var before = new silk.node.list( this.domNode, this.mount.name ); before.childNodes = this.childNodes; this.childNodes = this.parseDOM().childNodes; this.each( function() { if ( typeof( this.onDOMChange ) === "function" ) { this.onDOMChange( mutations ); } } ); /* console.log( "onDOMChange", this.mount.name ); console.log( "> before", before.getValue() ); console.log( "> after", this.getValue() ); */ this.mount.onDOMChange( this, before ); }, display: function( data, reset ) { return this.render( data, reset ); }, render: function( data, reset ) { var self = this; if ( ! ( data instanceof Array ) ) { //debugger } var data = ( data instanceof Array ) ? data : data.getArray(); this.mObserver.stop(); this.setValue( data, reset ); this.childNodes = this.parseDOM().childNodes; this.each( function() { if ( typeof( this.onRender ) === "function" ) { this.onRender(); } } ); this.mObserver.start(); } } ); silk.resource = xb.core.object.extend( { handlers: { }, ctor: function( config ) { this.done = false; this.config = config; this.mounts = {}; this.data = null; //this.init(); }, init: function() { }, save: function() { console.warn( "No save handler for", this.config.name ); return false; }, getResource: function( name ) { return this.config.dispatcher.getResource( name ); }, getMount: function( name, resourceName ) { if ( arguments.length > 1 ) { return this.config.dispatcher.getMount.apply( this.config.dispatcher, arguments ); } var mount = this.mounts[ name ]; return ( ( typeof( mount ) !== "undefined" ) ? mount : null ); }, add: function( name, domNode ) { var hName = name.split( "/" )[ 0 ]; var handler = this.handlers[ hName ]; if ( typeof( handler ) !== "function" ) { handler = this.handlers[ "*" ]; } if ( typeof( handler ) === "function" ) { if ( typeof( this.mounts[ name ] ) === "undefined" ) { this.mounts[ name ] = new handler( domNode, name, this ); } else { this.mounts[ name ].addDOMNode( domNode ); } return this.mounts[ name ]; } return null; }, load: function( data ) { this.data = data; console.log( "load data", this.mounts ); for ( var name in this.mounts ) { console.log( "Resource loaded, notifying 'mount'", name ); this.mounts[ name ].onLoad(); } this.done = true; return this; }, loadURL: function( url, params, callback ) { var params = ( ( typeof( params ) === "string" ) ? params : "" ); var http = new XMLHttpRequest(); http.open( "get", url, true ); // http.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" ); http.onreadystatechange = function() { if ( http.readyState == 4 ) { if ( http.status == 200 ) { var result = null; try { result = JSON.parse( http.responseText ); } catch( e ) { console.error( "Could not parse", url, e ); }; if ( typeof( callback ) === "function" ) { return callback.call( null, result ); } return result; } } }; return http.send( params ); }, putURL: function( url, params, callback ) { var params = ( ( typeof( params ) === "string" ) ? params : "" ); var http = new XMLHttpRequest(); http.open( "put", url, true ); // http.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" ); http.onreadystatechange = function() { if ( http.readyState == 4 ) { var result = null; try { result = JSON.parse( http.responseText ); } catch( e ) {}; if ( typeof( callback ) === "function" ) { return callback.call( null, http.status, result, http.responseText ); } return result; } }; return http.send( params ); } } ); silk.dispatcher = xb.core.object.extend( { ctor: function( mappings ) { var self = this; this.save = null; console.warn( "silk.dispatcher", "replaced save() with null. Dit doen we omdat de editor anders crashed (@save) als we niet geinitialiseerd zijn." ); this.mappings = ( ( typeof( mappings ) !== "undefined" ) ? mappings : {} ); this.resources = {}; document.addEventListener( "simply-content-loaded", function( evt ) { self.initResources(); } ); var query = ""; for ( var name in this.mappings ) { console.log( "addDataSource", name ); this.resources[ name ] = null; if ( typeof( editor ) === "object" ) { editor.addDataSource( name, this ); continue; } if ( query.length ) { query += ", "; } query += "[data-simply-data=" + name + "]"; } if ( query.length ) { var elms = document.querySelectorAll( query ); for ( var i = 0, il = elms.length; i < il; i++ ) { this.load( elms[ i ] ); } this.initResources(); } }, getResource: function( name ) { var config = this.resources[ name ]; if ( typeof( config ) !== "undefined" && config !== null ) { return config.resource; } return null; }, getMount: function( name, resourceName ) { var resource = this.getResource( resourceName ); if ( resource instanceof silk.resource ) { return resource.getMount( name ); } return null; }, load: function( elm ) { if ( this.save === null ) { console.warn( "Removing null @save()" ); delete this.save; } var resource = elm.getAttribute( "data-simply-data" ); var name = elm.getAttribute( "data-silk-mount" ); if ( name === null ) { name = elm.getAttribute( "data-simply-list" ); } console.log( "silk.dispatcher::load(", resource, name, ")" ); var config = this.resources[ resource ]; if ( config !== null && config.resource.done ) { console.error( "simply-edit is trying to reload us, but we were already done!" ); } if ( config === null ) { config = { dispatcher: this, elm: elm, resource: null, name: resource }; config.resource = this.mappings[ config.name ]( config ); } this.resources[ resource ] = config; config.resource.add( name, elm ); }, initResources: function() { console.warn( "Going to init all resources!", silk.freshLoad ); for( var r in this.resources ) { var resource = this.getResource( r ); if ( resource !== null && resource.__inited !== true ) { resource.init(); resource.__inited = true; } } }, delayedSave: function( counter ) { var self = this; if ( counter === 0 || counter < this.saveCounter ) { window.setTimeout( function() { self.delayedSave( this.saveCounter ); }, 10 ); } else { console.warn( "Going to save @", this.saveCounter, "!" ); for( var r in this.resources ) { var resource = this.getResource( r ); if ( resource !== null ) { resource.save(); } } this.saveCounter = 0; } }, get: function( elm ) { this.stash = [ null ]; var resourceName = elm.getAttribute( "data-simply-data" ); var mountName = elm.getAttribute( "data-simply-list" ); console.log( "silk.dispatcher::get(", resourceName, mountName, ")", this.stash ); return null; }, save: function( info ) { console.log( "silk.dispatcher::save(", info, ")" ); if ( typeof( this.saveCounter ) === "undefined" || this.saveCounter === 0 ) { this.saveCounter = 0; this.delayedSave( this.saveCounter ); } this.saveCounter += 1; return null; } } );