151 lines
3.1 KiB
JavaScript
151 lines
3.1 KiB
JavaScript
|
|
||
|
var VnObject = require('./object');
|
||
|
|
||
|
/**
|
||
|
* Base class for compilers.
|
||
|
*/
|
||
|
module.exports = new Class({
|
||
|
Extends: VnObject
|
||
|
|
||
|
,compile: function() {}
|
||
|
,postCompile: function() {}
|
||
|
,instantiate: function() {}
|
||
|
,preLink: function() {}
|
||
|
,link: function() {}
|
||
|
,connect: function() {}
|
||
|
,postLink: function() {}
|
||
|
,setProperty: function() {}
|
||
|
,free: function() {}
|
||
|
|
||
|
,initialize: function(builder) {
|
||
|
this._builder = builder;
|
||
|
this._interpoler = builder._interpoler;
|
||
|
this.parent();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if the passed attribute name it's an event.
|
||
|
*
|
||
|
* @param {String} attribute The attribute name
|
||
|
* @return {Boolean} %true if it's an event, otherwise %false
|
||
|
*/
|
||
|
,isEvent: function(attribute) {
|
||
|
return /^on-\w+/.test(attribute);
|
||
|
}
|
||
|
|
||
|
,isIdentifier: function(value) {
|
||
|
return /^[a-zA-Z_$][\w$]*$/.test(value);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Logs an error parsing the node.
|
||
|
*
|
||
|
* @param {String} error The error message template
|
||
|
* @param {...} varArgs The message template arguments
|
||
|
*/
|
||
|
,showError: function() {
|
||
|
this._builder.showError.apply(this._builder, arguments);
|
||
|
}
|
||
|
|
||
|
,_getMethod: function(value) {
|
||
|
// XXX: Compatibility with old methods
|
||
|
return this.isIdentifier(value)
|
||
|
? value
|
||
|
: this.fnExpr(value);
|
||
|
}
|
||
|
|
||
|
,bindMethod(handler, scope, isEvent) {
|
||
|
// XXX: Compatibility with old methods
|
||
|
if (typeof handler === 'string') {
|
||
|
const method = scope.thisArg[handler];
|
||
|
if (!method) {
|
||
|
this.showError(`Function '${handler}' not found`);
|
||
|
return undefined;
|
||
|
}
|
||
|
|
||
|
return method.bind(scope.thisArg);
|
||
|
}
|
||
|
|
||
|
return function($event) {
|
||
|
let handlerScope;
|
||
|
if (isEvent) {
|
||
|
handlerScope = Object.create(scope.$);
|
||
|
Object.assign(handlerScope, {$event});
|
||
|
} else
|
||
|
handlerScope = scope.$;
|
||
|
|
||
|
handler.call(this, handlerScope);
|
||
|
}.bind(scope.thisArg);
|
||
|
}
|
||
|
|
||
|
,matchExpr(value) {
|
||
|
const match = /^{{(.*)}}$/.exec(value);
|
||
|
if (!match) return null;
|
||
|
return this.fnExpr(match[1]);
|
||
|
}
|
||
|
|
||
|
,modelExpr(expr) {
|
||
|
try {
|
||
|
return new Function('$scope', '$value',
|
||
|
`"use strict"; $scope.${expr} = $value;`
|
||
|
);
|
||
|
} catch (err) {
|
||
|
this.showError(`${err.message}:`, expr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
,exprRegex: /^{{((?:(?!}}).)*)}}$/
|
||
|
,exprRegexMulti: /{{((?:(?!}}).)*)}}/g
|
||
|
|
||
|
,isExpr(expr, isMulti) {
|
||
|
return isMulti
|
||
|
? this.exprRegexMulti.test(expr)
|
||
|
: this.exprRegex.test(expr);
|
||
|
}
|
||
|
|
||
|
,compileExpr(context, property, value, isMulti) {
|
||
|
const exprContext = {
|
||
|
context,
|
||
|
property,
|
||
|
value
|
||
|
};
|
||
|
|
||
|
if (isMulti) {
|
||
|
let i = 0;
|
||
|
const self = this;
|
||
|
exprContext.exprs = [];
|
||
|
exprContext.template = value.replace(this.exprRegexMulti,
|
||
|
function(match, capture) {
|
||
|
exprContext.exprs.push(self.fnExpr(capture));
|
||
|
return `{{${i++}}}`;
|
||
|
});
|
||
|
} else {
|
||
|
const match = this.exprRegex.exec(value);
|
||
|
exprContext.expr = this.fnExpr(match[1]);
|
||
|
}
|
||
|
|
||
|
this._builder._exprContexts.push(exprContext);
|
||
|
}
|
||
|
|
||
|
,fnExpr(expr) {
|
||
|
try {
|
||
|
return new Function('$scope',
|
||
|
`with($scope) { return ${expr}; }`
|
||
|
);
|
||
|
} catch (err) {
|
||
|
this.showError(`${err.message}:`, expr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
,_translateValue: function(value) {
|
||
|
var chr = value.charAt(0);
|
||
|
|
||
|
if (chr === '_')
|
||
|
return _(value.substr(1));
|
||
|
else if (chr === '\\' && value.charAt(1) === '_')
|
||
|
return value.substr(1);
|
||
|
|
||
|
return value;
|
||
|
}
|
||
|
});
|