walk : function(endpoint, root, callback) { if(R.isFunction(root) && R.isUndefined(callback)) { callback = root; root = null; } var self = this; var node = root || this.table; var temp = endpoint.split(this.delimiter); var chunks = [ ]; // Filter false values. for(var i in temp) { temp[i] && chunks.push(temp[i]); } chunks = R.iterate(chunks); // The faux iterator exists for callback uniformity. callback && callback(node, node.name, R.iterate()); var match; while(match = this.test(node, chunks)) { node = match.node; callback && callback(node, match.match, match.groups, match.segments); if(node.terminal) { var path = (chunks.pop(chunks.index()+1, chunks.remaining()) || [ ]) .join(this.delimiter); node = node.segments['(.*)']; callback && callback(node, path); } }; if(chunks.remaining()) { throw new Error('Elixir.walk: Path not found or improperly formed. ' + 'Not all chunks have been consumed.'); } return node; },
make : function(endpoint, root, callback) { if(R.isFunction(root) && R.isUndefined(callback)) { callback = root; root = null; } var self = this; var node = root || this.table; endpoint = R.tokenize(endpoint, this.tokens); // Remove each occurance of two or more `this.terminator`s in a row and // replace them with only two `this.terminator`s. Also, only clamp the // beginning of `endpoint`. endpoint = R.sanitize(endpoint, this.terminator, 2, 2, -1); var parts = endpoint.split(this.terminator + this.terminator); if( (parts.length > 1) && !(parts[1] === '') ) { throw new Error('Elixir.make: Improperly formed endpoint.'); } var resource = parts[0]; var terminal = ( parts[1] === '' ? true : false ); if(terminal) { resource += this.delimiter + '(.*)'; } resource = R.sanitize(resource, this.delimiter); resource = R.regify(resource); var segments = R.iterate(R.split(resource, this.delimiter)); // Add the root node. callback && callback(self.ensure(node)); // Iterate through other nodes. for(var segment; segment = segments.next();) { if(node.terminal && !( segment === '(.*)' ) ) { throw new Error('Elixir.make: Trying to travel past a terminal ' + 'node.'); } if( terminal && !segments.remaining() ) { if( this.isPertinent(node) && !( segment === '(.*)' )) { throw new Error('Elixir.make: Trying to terminate a pertinent ' + 'node.'); } node.terminal = true; } node = self.ensure(node, segment); callback && callback(node); } return node; },
modifyState : function(node, state, method, handlers, action) { if(R.isUndefined(action)) { action = handlers; handlers = method; method = null; } if(!R.contains(this.states, state)) { throw new Error('Unsupported State: ' + state); } // TODO: This if statement should be more explicit. if(R.isFunction(handlers)) { handlers = [ handlers ]; } // TODO: Verify parameters here. var states = this.getStates(node, method); switch(action) { case 'add' : R.flatten(handlers, states[state]); break; case 'remove' : var replace = [ ]; var removed = [ ]; // Iterate through existing handlers and see if they exist in the // array of handlers passed as a parameter. If true, add them to // the list of handlers to be removed, else, add them to the // replacement list. if( !handlers ) { handlers = states[state]; } R.each(states[state], function(handler) { ( R.contains(handlers, handler) ? removed : replace ).push(handler); }); states[state] = replace; if( !this.isPertinent(node) ) { this.prune(node, { parents: true, children : true }); } return removed; default : throw new Error('Elixir.modifyState: Unsupported action - '+action); } },