var Property = require('./property'), View = require('./view'), statham = require('statham'), createSpec = require('spec-js'); function TemplaterProperty(){} TemplaterProperty = createSpec(TemplaterProperty, Property); TemplaterProperty.prototype.trackKeys = true; function findValueIn(value, source){ var isArray = Array.isArray(source); for(var key in source){ if(isArray && isNaN(key)){ continue; } if(source[key] === value){ return key; } } } TemplaterProperty.prototype._immediate = true; TemplaterProperty.prototype.watchChanges = 'structure value'; TemplaterProperty.prototype.hasChanged = function(){ var lastKeys = this._lastValue._lastKeys || [], changes = this._lastValue.update(this.value), watched = this.watchChanges.split(' '), newKeys = [], keysChanged; if(this._sourcePathInfo && this._sourcePathInfo.subPaths){
} tokens.forEach(function(token){ token.evaluate(scope); }) } function createOpperatorTokeniser(Constructor, opperator) { return function(substring){ if(substring.indexOf(opperator) === 0){ return new Constructor(opperator, opperator.length); } }; } function PipeToken(){} PipeToken = createSpec(PipeToken, Token); PipeToken.prototype.name = 'PipeToken'; PipeToken.tokenPrecedence = 1; PipeToken.prototype.parsePrecedence = 5; PipeToken.tokenise = createOpperatorTokeniser(PipeToken, '|'); PipeToken.prototype.evaluate = function(scope, args) { this.result = '|'; }; function ParenthesesCloseToken(){} ParenthesesCloseToken = createSpec(ParenthesesCloseToken, Token); ParenthesesCloseToken.tokenPrecedence = 1; ParenthesesCloseToken.prototype.parsePrecedence = 10; ParenthesesCloseToken.prototype.name = 'ParenthesesCloseToken' ParenthesesCloseToken.tokenise = function(substring) { if(substring.charAt(0) === ')'){
viewItem[key] = description[key]; } } } /** ## ViewItem The base constructor for all gaffa ViewItems. Views, Behaviours, and Actions inherrit from ViewItem. */ function ViewItem(viewItemDescription){ inflateViewItem(this, viewItemDescription); } ViewItem = createSpec(ViewItem, Bindable); /** ## .path the base path for a viewItem. Any bindings on a ViewItem will recursivly resolve through the ViewItems parent's paths. Eg: // Some ViewItems var viewItem1 = new views.button(), viewItem2 = new actions.set(); // Give viewItem1 a path.
action.bind(parent, scope); } function triggerActions(actions, parent, scope, event) { if(Array.isArray(actions)){ for(var i = 0; i < actions.length; i++) { triggerAction(actions[i], parent, scope, event); } }else if(actions instanceof Action){ triggerAction(actions, parent, scope, event); } } function Action(actionDescription){ } Action = createSpec(Action, ViewItem); Action.trigger = triggerActions; Action.prototype.trigger = function(){ throw 'Nothing is implemented for this action (' + this.constructor.name + ')'; }; Action.prototype.condition = new Property({ value: true }); // Because actions shouldn't neccisarily debind untill they are complete, // They have an odd debind impementation. Action.prototype.debind = function(){ if(!this._complete && !this._destroyed){ return; } this.complete();
var createSpec = require('spec-js'), ViewItem = require('./viewItem'); function Behaviour(behaviourDescription){} Behaviour = createSpec(Behaviour, ViewItem); Behaviour.prototype.bind = function(parent){ ViewItem.prototype.bind.apply(this, arguments); } module.exports = Behaviour;
return scope; } function bindViewEvent(view, eventName){ return view.gaffa.events.on(eventName, view.renderedElement, function (event) { view.triggerActions(eventName, createEventedActionScope(view, event), event); }); } function View(viewDescription){ var view = this; view.behaviours = view.behaviours || []; this.consuela = new Consuela(); } View = createSpec(View, ViewItem); function watchElements(view){ for(var key in view){ if(!view.hasOwnProperty(key)){ return; } if(crel.isElement(view[key])){ view.consuela.watch(view[key]); } } } function bindViewEvents(view){ for(var key in view.actions){ var actions = view.actions[key],
} referenceItem = referenceItem.parent; } return gedi.paths.resolve.apply(this, paths.reverse()); } var iuid = 0; function Bindable(){ this.setMaxListeners(1000); // instance unique ID this.__iuid = iuid++; } Bindable = createSpec(Bindable, EventEmitter); Bindable.bindables = {}; Bindable.getByIuid = function(id){ return this.bindables[id]; }; Bindable.prototype.getPath = function(){ return getItemPath(this); }; Bindable.prototype.getDataAtPath = function(){ if(!this.gaffa){ return; } return this.gaffa.model.get(getItemPath(this)); }; Bindable.prototype.toJSON = function(){ var tempObject = jsonConverter(this, this.__serialiseExclude__, this.__serialiseInclude__);
if(identifier != null){ return identifier; } } function createKeywordTokeniser(Constructor, keyword){ return function(substring){ substring = isIdentifier(substring); if (substring === keyword) { return new Constructor(substring, substring.length); } }; } function StringToken(){} StringToken = createSpec(StringToken, Token); StringToken.tokenPrecedence = 2; StringToken.prototype.parsePrecedence = 2; StringToken.prototype.stringTerminal = '"'; StringToken.prototype.name = 'StringToken'; StringToken.prototype.static = true; StringToken.tokenise = function (substring) { if (substring.charAt(0) === this.prototype.stringTerminal) { var index = 0, escapes = 0; while (substring.charAt(++index) !== this.prototype.stringTerminal) { if(index >= substring.length){ throw "Unclosed " + this.name; }
}else{ for(var key in propertyDescription){ if( !propertyDescription.hasOwnProperty(key) || propertyDescription instanceof Property && ( ~propertyDescription.__serialiseExclude__.indexOf(key) || ~excludeProps.indexOf(key) ) ){ continue; } this[key] = propertyDescription[key]; } } } Property = createSpec(Property, Bindable); Property.prototype.watchChanges = 'value keys structure reference type'; Property.prototype.hasChanged = function(){ var changes = this._lastValue.update(this.value), watched = this.watchChanges.split(' '); for(var i = 0; i < watched.length; i++){ if(changes[watched[i]]){ return true; } } }; Property.prototype.set = function(value, isDirty, scope){ if(!this._bound){ this.value = value; return;
},{}],2:[function(require,module,exports){ var Lang = require('lang-js'), Token = Lang.Token, global = require('./global'), createSpec = require('spec-js'); var createNestingParser = Lang.createNestingParser, Token = Lang.Token, Scope = Lang.Scope; function combinedTokensResult(tokens){ if(tokens.length === 1){ return tokens[0].result; } return tokens.reduce(function(result, token){ return result += token.result; },''); } function evaluateTokens(tokens, scope){ if(!tokens){ return; } tokens.forEach(function(token){ token.evaluate(scope); }) } function createOpperatorTokeniser(Constructor, opperator) { return function(substring){ if(substring.indexOf(opperator) === 0){ return new Constructor(opperator, opperator.length); } }; } function PipeToken(){} PipeToken = createSpec(PipeToken, Token); PipeToken.prototype.name = 'PipeToken'; PipeToken.tokenPrecedence = 1; PipeToken.prototype.parsePrecedence = 5; PipeToken.tokenise = createOpperatorTokeniser(PipeToken, '|'); PipeToken.prototype.parse = function(tokens, position){ this.leftTokens = tokens.splice(0, position); var rightIndex = 1; while(tokens[rightIndex] && !(tokens[rightIndex] instanceof PipeToken)){ rightIndex++; } this.rightTokens = tokens.splice(1, rightIndex - 1); if(!this.leftTokens){ throw "Invalid syntax, expected token before |"; } if(!this.rightTokens){ throw "Invalid syntax, expected token after |"; } }; PipeToken.prototype.evaluate = function(scope, args) { evaluateTokens(this.leftTokens, scope); evaluateTokens(this.rightTokens, scope); var leftTokens = this.leftTokens, rightTokens = this.rightTokens; if(leftTokens.length === 1 && leftTokens[0] instanceof PipeToken){ // concat this.result = leftTokens[0].result.slice(); }else{ this.result = []; if(leftTokens.length){ this.result.push(combinedTokensResult(leftTokens)); } } this.result.push(combinedTokensResult(rightTokens)); }; function ParenthesesCloseToken(){} ParenthesesCloseToken = createSpec(ParenthesesCloseToken, Token); ParenthesesCloseToken.tokenPrecedence = 1; ParenthesesCloseToken.prototype.parsePrecedence = 10; ParenthesesCloseToken.prototype.name = 'ParenthesesCloseToken' ParenthesesCloseToken.tokenise = function(substring) { if(substring.charAt(0) === ')'){ return new ParenthesesCloseToken(substring.charAt(0), 1); } } function ParenthesesOpenToken(){} ParenthesesOpenToken = createSpec(ParenthesesOpenToken, Token); ParenthesesOpenToken.tokenPrecedence = 1; ParenthesesOpenToken.prototype.parsePrecedence = 3; ParenthesesOpenToken.prototype.name = 'ParenthesesOpenToken' ParenthesesOpenToken.tokenise = function(substring) { if(substring.charAt(0) === '('){ return new ParenthesesOpenToken(substring.charAt(0), 1); } } ParenthesesOpenToken.prototype.parse = createNestingParser(ParenthesesCloseToken); ParenthesesOpenToken.prototype.evaluate = function(scope){ for(var i = 0; i < this.childTokens.length; i++){ this.childTokens[i].evaluate(scope); } if(this.childTokens.length === 1 && this.childTokens[0] instanceof PipeToken){ this.result = this.childTokens[0].result; }else{ this.result = [combinedTokensResult(this.childTokens)]; } } function WordToken(){} WordToken = createSpec(WordToken, Token); WordToken.tokenPrecedence = 100; // very last thing always WordToken.prototype.parsePrecedence = 1; WordToken.prototype.name = 'WordToken'; WordToken.tokenise = function(substring) { var character = substring.slice(0,1), length = 1; if(character === '\\'){ if(substring.charAt(1) !== '\\'){ character = substring.charAt(1); } length++; } return new WordToken(character, length); }; WordToken.prototype.parse = function(tokens, position){ var index = 0; while(tokens[position + index + 1] && tokens[position + index + 1].name === 'WordToken'){ index++ } this.childTokens = tokens.splice(position + 1, index); }; WordToken.prototype.evaluate = function(scope){ this.result = this.original; for(var i = 0; i < this.childTokens.length; i++){ this.result+= this.childTokens[i].original; } }; function PlaceholderToken(){} PlaceholderToken = createSpec(PlaceholderToken, Token); PlaceholderToken.tokenPrecedence = 1; PlaceholderToken.prototype.parsePrecedence = 2; PlaceholderToken.prototype.name = 'PlaceholderToken'; PlaceholderToken.regex = /^(\{.+?\})/; PlaceholderToken.tokenise = function(substring){ var match = substring.match(PlaceholderToken.regex); if(match){ if(!match[1].match(/^\{\w+\}$/)){ throw "Invalid placeholder name. Placeholders may only contain word characters"; } var token = new PlaceholderToken(match[1], match[1].length); token.key = token.original.slice(1,-1); return token; } }; PlaceholderToken.prototype.evaluate = function(scope){ var result = scope.get(this.original.slice(1,-1)); if(result instanceof Term){ result = ''; } this.result = result; }; function EvaluateToken(){} EvaluateToken = createSpec(EvaluateToken, Token); EvaluateToken.tokenPrecedence = 1; EvaluateToken.prototype.parsePrecedence = 4; EvaluateToken.prototype.name = 'EvaluateToken'; EvaluateToken.regex = /^~(\w+)?(?:\(|\||\)|\s|$)/; EvaluateToken.tokenise = function(substring){ var match = substring.match(EvaluateToken.regex); if(!match){ return; } var term = match[1]; var token = new EvaluateToken(match[1], match[1].length + 1); token.term = term; return token; }; EvaluateToken.prototype.parse = function(tokens, position){ if(tokens[position+1] instanceof ParenthesesOpenToken){ this.argsToken = tokens.splice(position+1,1).pop(); } }; EvaluateToken.prototype.evaluate = function(scope){ var term = scope.get(this.term), fn, args = []; if(this.argsToken){ this.argsToken.evaluate(scope); args = this.argsToken.result; } if(term instanceof Term){ this.result = scope.evaluateTerm(term, scope, args); }else{ this.result = scope.callWith(term, args); } }; function Term(key, expression){ var parts = key.match(/(\w+)(?:\((.*?)\))?(?:\||\)|\s|$)/); if(!parts){ throw "Invalid term definition: " + key; } this.term = parts[1]; this.parameters = parts[2] ? parts[2].split('|') : []; this.expression = expression; } var tokenConverters = [ ParenthesesCloseToken, ParenthesesOpenToken, WordToken, PlaceholderToken, EvaluateToken, PipeToken ]; function convertTerms(termDefinitions, terms){ terms || (terms = {}); for(var key in termDefinitions){ var term = new Term(key, termDefinitions[key]); terms[term.term] = term; } return terms; } var SeeThreepio = function(termDefinitions){ var seeThreepio = {}, lang = new Lang(), terms = convertTerms(termDefinitions); function evaluateTerm(term, scope, args){ for(var i = 0; i < term.parameters.length; i++){ var paremeter = term.parameters[i]; scope.set(paremeter, args[i]); } var tokens = lang.evaluate(term.expression, scope, tokenConverters, true); return '' + combinedTokensResult(tokens); } seeThreepio.lang = lang; seeThreepio.tokenConverters = tokenConverters; seeThreepio.global = global; seeThreepio.tokenise = function(expression){ return seeThreepio.lang.tokenise(expression, seeThreepio.tokenConverters); } seeThreepio.get = function(termName, args){ var scope = new Lang.Scope(); scope.add(this.global).add(terms); scope.evaluateTerm = evaluateTerm; var term = scope.get(termName); if(!term){ // ToDo, something nicer than throw throw "term not defined: " + termName; } return evaluateTerm(term, scope, args); }; seeThreepio.addTerms = function(termDefinitions){ convertTerms(termDefinitions, terms); }; seeThreepio.replaceTerms = function(termDefinitions){ terms = convertTerms(termDefinitions); }; return seeThreepio; }; module.exports = SeeThreepio; },{"./global":1,"lang-js":3,"spec-js":5}],3:[function(require,module,exports){
*/ var createSpec = require('spec-js'), View = require('./view'), ViewContainer = require('./viewContainer'), Property = require('./property'); function ContainerView(viewDescription){ this.views = this.views || {}; this.views.content = new ViewContainer(this.views.content); this.on('bind', function(){ for(var key in this.views){ var viewContainer = this.views[key]; if(viewContainer instanceof ViewContainer){ viewContainer.bind(this); } } }); } ContainerView = createSpec(ContainerView, View); ContainerView.prototype.renderChildren = new Property({ update: function(view, value){ for(var key in view.views){ view.views[key][value ? 'render' : 'derender'](); } }, value: true }); module.exports = ContainerView;