671 lines
22 KiB
JavaScript
671 lines
22 KiB
JavaScript
/* This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
this repository contains the full copyright notices and license terms. */
|
|
(function() {
|
|
'use strict';
|
|
|
|
Sao.PYSON = {};
|
|
|
|
Sao.PYSON.PYSON = Sao.class_(Object, {
|
|
init: function() {
|
|
},
|
|
pyson: function() {
|
|
throw 'NotImplementedError';
|
|
},
|
|
types: function() {
|
|
throw 'NotImplementedError';
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.PYSON.eval_ = function(value, context) {
|
|
throw 'NotImplementedError';
|
|
};
|
|
|
|
Sao.PYSON.Encoder = Sao.class_(Object, {
|
|
encode: function(pyson) {
|
|
return JSON.stringify(pyson, function(k, v) {
|
|
if (v instanceof Sao.PYSON.PYSON) {
|
|
return v.pyson();
|
|
} else if (v instanceof Date) {
|
|
if (v.isDate) {
|
|
return Sao.PYSON.Date(
|
|
v.getFullYear(),
|
|
v.getMonth(),
|
|
v.getDate()).pyson();
|
|
} else {
|
|
return Sao.PYSON.DateTime(
|
|
v.getFullYear(),
|
|
v.getMonth(),
|
|
v.getDate(),
|
|
v.getHours(),
|
|
v.getMinutes(),
|
|
v.getSeconds(),
|
|
v.getMilliseconds()).pyson();
|
|
}
|
|
}
|
|
return v;
|
|
});
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.Decoder = Sao.class_(Object, {
|
|
init: function(context) {
|
|
this.__context = context || {};
|
|
},
|
|
decode: function(str) {
|
|
var reviver = function(k, v) {
|
|
if (typeof v == 'object' && v !== null) {
|
|
var cls = Sao.PYSON[v.__class__];
|
|
if (cls) {
|
|
return cls.eval_(v, this.__context);
|
|
}
|
|
}
|
|
return v;
|
|
};
|
|
return JSON.parse(str, reviver.bind(this));
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.Eval = Sao.class_(Sao.PYSON.PYSON, {
|
|
init: function(value, default_) {
|
|
if (default_ === undefined) {
|
|
default_ = '';
|
|
}
|
|
Sao.PYSON.Eval._super.init.call(this);
|
|
this._value = value;
|
|
this._default = default_;
|
|
},
|
|
pyson: function() {
|
|
return {
|
|
'__class__': 'Eval',
|
|
'v': this._value,
|
|
'd': this._default
|
|
};
|
|
},
|
|
types: function() {
|
|
if (this._default instanceof Sao.PYSON.PYSON) {
|
|
return this._default.types();
|
|
} else {
|
|
return [typeof this._default];
|
|
}
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.Eval.eval_ = function(value, context) {
|
|
if (value.v in context) {
|
|
return context[value.v];
|
|
} else {
|
|
return value.d;
|
|
}
|
|
};
|
|
|
|
Sao.PYSON.Not = Sao.class_(Sao.PYSON.PYSON, {
|
|
init: function(value) {
|
|
Sao.PYSON.Not._super.init.call(this);
|
|
if (value instanceof Sao.PYSON.PYSON) {
|
|
if (jQuery(value.types()).not(['boolean']).length ||
|
|
jQuery(['boolean']).not(value.types()).length) {
|
|
throw 'value must be boolean';
|
|
}
|
|
} else {
|
|
if (typeof value != 'boolean') {
|
|
throw 'value must be boolean';
|
|
}
|
|
}
|
|
this._value = value;
|
|
},
|
|
pyson: function() {
|
|
return {
|
|
'__class__': 'Not',
|
|
'v': this._value
|
|
};
|
|
},
|
|
types: function() {
|
|
return ['boolean'];
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.Not.eval_ = function(value, context) {
|
|
return !value.v;
|
|
};
|
|
|
|
Sao.PYSON.Bool = Sao.class_(Sao.PYSON.PYSON, {
|
|
init: function(value) {
|
|
Sao.PYSON.Bool._super.init.call(this);
|
|
this._value = value;
|
|
},
|
|
pyson: function() {
|
|
return {
|
|
'__class__': 'Bool',
|
|
'v': this._value
|
|
};
|
|
},
|
|
types: function() {
|
|
return ['boolean'];
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.Bool.eval_ = function(value, context) {
|
|
if (value.v instanceof Object) {
|
|
return !jQuery.isEmptyObject(value.v);
|
|
} else {
|
|
return Boolean(value.v);
|
|
}
|
|
};
|
|
|
|
|
|
Sao.PYSON.And = Sao.class_(Sao.PYSON.PYSON, {
|
|
init: function(statements) {
|
|
Sao.PYSON.And._super.init.call(this);
|
|
for (var i = 0, len = statements.length; i < len; i++) {
|
|
var statement = statements[i];
|
|
if (statement instanceof Sao.PYSON.PYSON) {
|
|
if (jQuery(statement.types()).not(['boolean']).length ||
|
|
jQuery(['boolean']).not(statement.types()).length) {
|
|
throw 'statement must be boolean';
|
|
}
|
|
} else {
|
|
if (typeof statement != 'boolean') {
|
|
throw 'statement must be boolean';
|
|
}
|
|
}
|
|
}
|
|
if (statements.length < 2) {
|
|
throw 'must have at least 2 statements';
|
|
}
|
|
this._statements = statements;
|
|
},
|
|
pyson: function() {
|
|
return {
|
|
'__class__': 'And',
|
|
's': this._statements
|
|
};
|
|
},
|
|
types: function() {
|
|
return ['boolean'];
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.And.eval_ = function(value, context) {
|
|
var result = true;
|
|
for (var i = 0, len = value.s.length; i < len; i++) {
|
|
var statement = value.s[i];
|
|
result = result && statement;
|
|
}
|
|
return result;
|
|
};
|
|
|
|
|
|
Sao.PYSON.Or = Sao.class_(Sao.PYSON.And, {
|
|
pyson: function() {
|
|
var result = Sao.PYSON.Or._super.pyson.call(this);
|
|
result.__class__ = 'Or';
|
|
return result;
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.Or.eval_ = function(value, context) {
|
|
var result = false;
|
|
for (var i = 0, len = value.s.length; i < len; i++) {
|
|
var statement = value.s[i];
|
|
result = result || statement;
|
|
}
|
|
return result;
|
|
};
|
|
|
|
Sao.PYSON.Equal = Sao.class_(Sao.PYSON.PYSON, {
|
|
init: function(statement1, statement2) {
|
|
Sao.PYSON.Equal._super.init.call(this);
|
|
var types1, types2;
|
|
if (statement1 instanceof Sao.PYSON.PYSON) {
|
|
types1 = statement1.types();
|
|
} else {
|
|
types1 = [typeof statement1];
|
|
}
|
|
if (statement2 instanceof Sao.PYSON.PYSON) {
|
|
types2 = statement2.types();
|
|
} else {
|
|
types2 = [typeof statement2];
|
|
}
|
|
if (jQuery(types1).not(types2).length ||
|
|
jQuery(types2).not(types1).length) {
|
|
throw 'statements must have the same type';
|
|
}
|
|
this._statement1 = statement1;
|
|
this._statement2 = statement2;
|
|
},
|
|
pyson: function() {
|
|
return {
|
|
'__class__': 'Equal',
|
|
's1': this._statement1,
|
|
's2': this._statement2
|
|
};
|
|
},
|
|
types: function() {
|
|
return ['boolean'];
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.Equal.eval_ = function(value, context) {
|
|
return value.s1 == value.s2;
|
|
};
|
|
|
|
Sao.PYSON.Greater = Sao.class_(Sao.PYSON.PYSON, {
|
|
init: function(statement1, statement2, equal) {
|
|
Sao.PYSON.Greater._super.init.call(this);
|
|
var statements = [statement1, statement2];
|
|
for (var i = 0; i < 2; i++) {
|
|
var statement = statements[i];
|
|
if (statement instanceof Sao.PYSON.PYSON) {
|
|
if (jQuery(statement).not(['number']).length) {
|
|
throw 'statement must be an integer or a float';
|
|
}
|
|
} else {
|
|
if (typeof statement != 'number') {
|
|
throw 'statement must be an integer or a float';
|
|
}
|
|
}
|
|
}
|
|
if (equal === undefined) {
|
|
equal = false;
|
|
}
|
|
if (equal instanceof Sao.PYSON.PYSON) {
|
|
if (jQuery(equal.types()).not(['boolean']).length ||
|
|
jQuery(['boolean']).not(equal.types()).length) {
|
|
throw 'equal must be boolean';
|
|
}
|
|
} else {
|
|
if (typeof equal != 'boolean') {
|
|
throw 'equal must be boolean';
|
|
}
|
|
}
|
|
this._statement1 = statement1;
|
|
this._statement2 = statement2;
|
|
this._equal = equal;
|
|
},
|
|
pyson: function() {
|
|
return {
|
|
'__class__': 'Greater',
|
|
's1': this._statement1,
|
|
's2': this._statement2,
|
|
'e': this._equal
|
|
};
|
|
},
|
|
types: function() {
|
|
return ['boolean'];
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.Greater._convert = function(value) {
|
|
value = jQuery.extend({}, value);
|
|
value.s1 = Number(value.s1);
|
|
value.s2 = Number(value.s2);
|
|
return value;
|
|
};
|
|
|
|
Sao.PYSON.Greater.eval_ = function(value, context) {
|
|
value = Sao.PYSON.Greater._convert(value);
|
|
if (value.e) {
|
|
return value.s1 >= value.s2;
|
|
} else {
|
|
return value.s1 > value.s2;
|
|
}
|
|
};
|
|
|
|
Sao.PYSON.Less = Sao.class_(Sao.PYSON.Greater, {
|
|
pyson: function() {
|
|
var result = Sao.PYSON.Less._super.pyson.call(this);
|
|
result.__class__ = 'Less';
|
|
return result;
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.Less._convert = Sao.PYSON.Greater._convert;
|
|
|
|
Sao.PYSON.Less.eval_ = function(value, context) {
|
|
value = Sao.PYSON.Less._convert(value);
|
|
if (value.e) {
|
|
return value.s1 <= value.s2;
|
|
} else {
|
|
return value.s1 < value.s2;
|
|
}
|
|
};
|
|
|
|
Sao.PYSON.If = Sao.class_(Sao.PYSON.PYSON, {
|
|
init: function(condition, then_statement, else_statement) {
|
|
Sao.PYSON.If._super.init.call(this);
|
|
if (condition instanceof Sao.PYSON.PYSON) {
|
|
if (jQuery(condition.types()).not(['boolean']).length ||
|
|
jQuery(['boolean']).not(condition.types()).length) {
|
|
throw 'condition must be boolean';
|
|
}
|
|
} else {
|
|
if (typeof condition != 'boolean') {
|
|
throw 'condition must be boolean';
|
|
}
|
|
}
|
|
var then_types, else_types;
|
|
if (then_statement instanceof Sao.PYSON.PYSON) {
|
|
then_types = then_statement.types();
|
|
} else {
|
|
then_types = [typeof then_statement];
|
|
}
|
|
if (else_statement === undefined) {
|
|
else_statement = null;
|
|
}
|
|
if (else_statement instanceof Sao.PYSON.PYSON) {
|
|
else_types = else_statement.types();
|
|
} else {
|
|
else_types = [typeof else_statement];
|
|
}
|
|
if (jQuery(then_types).not(else_types).length ||
|
|
jQuery(else_types).not(then_types).length) {
|
|
throw 'then and else statements must be the same type';
|
|
}
|
|
this._condition = condition;
|
|
this._then_statement = then_statement;
|
|
this._else_statement = else_statement;
|
|
},
|
|
pyson: function() {
|
|
return {
|
|
'__class__': 'If',
|
|
'c': this._condition,
|
|
't': this._then_statement,
|
|
'e': this._else_statement
|
|
};
|
|
},
|
|
types: function() {
|
|
if (this._then_statement instanceof Sao.PYSON.PYSON) {
|
|
return this._then_statement.types();
|
|
} else {
|
|
return [typeof this._then_statement];
|
|
}
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.If.eval_ = function(value, context) {
|
|
if (value.c) {
|
|
return value.t;
|
|
} else {
|
|
return value.e;
|
|
}
|
|
};
|
|
|
|
Sao.PYSON.Get = Sao.class_(Sao.PYSON.PYSON, {
|
|
init: function(obj, key, default_) {
|
|
Sao.PYSON.Get._super.init.call(this);
|
|
if (default_ === undefined) {
|
|
default_ = null;
|
|
}
|
|
if (obj instanceof Sao.PYSON.PYSON) {
|
|
if (jQuery(obj.types()).not(['object']).length ||
|
|
jQuery(['object']).not(obj.types()).length) {
|
|
throw 'obj must be a dict';
|
|
}
|
|
} else {
|
|
if (!(obj instanceof Object)) {
|
|
throw 'obj must be a dict';
|
|
}
|
|
}
|
|
this._obj = obj;
|
|
if (key instanceof Sao.PYSON.PYSON) {
|
|
if (jQuery(key.types()).not(['string']).length ||
|
|
jQuery(['string']).not(key.types()).length) {
|
|
throw 'key must be a string';
|
|
}
|
|
} else {
|
|
if (typeof key != 'string') {
|
|
throw 'key must be a string';
|
|
}
|
|
}
|
|
this._key = key;
|
|
this._default = default_;
|
|
},
|
|
pyson: function() {
|
|
return {
|
|
'__class__': 'Get',
|
|
'v': this._obj,
|
|
'k': this._key,
|
|
'd': this._default
|
|
};
|
|
},
|
|
types: function() {
|
|
if (this._default instanceof Sao.PYSON.PYSON) {
|
|
return this._default.types();
|
|
} else {
|
|
return [typeof this._default];
|
|
}
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.Get.eval_ = function(value, context) {
|
|
if (value.k in value.v) {
|
|
return value.v[value.k];
|
|
} else {
|
|
return value.d;
|
|
}
|
|
};
|
|
|
|
Sao.PYSON.In = Sao.class_(Sao.PYSON.PYSON, {
|
|
init: function(key, obj) {
|
|
Sao.PYSON.In._super.init.call(this);
|
|
if (key instanceof Sao.PYSON.PYSON) {
|
|
if (jQuery(key.types()).not(['string', 'number']).length) {
|
|
throw 'key must be a string or a number';
|
|
}
|
|
} else {
|
|
if (!~['string', 'number'].indexOf(typeof key)) {
|
|
throw 'key must be a string or a number';
|
|
}
|
|
}
|
|
if (obj instanceof Sao.PYSON.PYSON) {
|
|
if (jQuery(obj.types()).not(['object']).length ||
|
|
jQuery(['object']).not(obj.types()).length) {
|
|
throw 'obj must be a dict or a list';
|
|
}
|
|
} else {
|
|
if (!(obj instanceof Object)) {
|
|
throw 'obj must be a dict or a list';
|
|
}
|
|
}
|
|
this._key = key;
|
|
this._obj = obj;
|
|
},
|
|
pyson: function() {
|
|
return {'__class__': 'In',
|
|
'k': this._key,
|
|
'v': this._obj
|
|
};
|
|
},
|
|
types: function() {
|
|
return ['boolean'];
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.In.eval_ = function(value, context) {
|
|
if (value.v.indexOf) {
|
|
return Boolean(~value.v.indexOf(value.k));
|
|
} else {
|
|
return !!value.v[value.k];
|
|
}
|
|
};
|
|
|
|
Sao.PYSON.Date = Sao.class_(Sao.PYSON.PYSON, {
|
|
init: function(year, month, day, delta_years, delta_months, delta_days)
|
|
{
|
|
Sao.PYSON.Date._super.init.call(this);
|
|
if (year === undefined) year = null;
|
|
if (month === undefined) month = null;
|
|
if (day === undefined) day = null;
|
|
if (delta_years === undefined) delta_years = 0;
|
|
if (delta_months === undefined) delta_months = 0;
|
|
if (delta_days === undefined) delta_days = 0;
|
|
|
|
this._test(year, 'year');
|
|
this._test(month, 'month');
|
|
this._test(day, 'day');
|
|
this._test(delta_years, 'delta_years');
|
|
this._test(delta_days, 'delta_days');
|
|
this._test(delta_months, 'delta_months');
|
|
|
|
this._year = year;
|
|
this._month = month;
|
|
this._day = day;
|
|
this._delta_years = delta_years;
|
|
this._delta_months = delta_months;
|
|
this._delta_days = delta_days;
|
|
},
|
|
pyson: function() {
|
|
return {
|
|
'__class__': 'Date',
|
|
'y': this._year,
|
|
'M': this._month,
|
|
'd': this._day,
|
|
'dy': this._delta_years,
|
|
'dM': this._delta_months,
|
|
'dd': this._delta_days
|
|
};
|
|
},
|
|
types: function() {
|
|
return ['object'];
|
|
},
|
|
_test: function(value, name) {
|
|
if (value instanceof Sao.PYSON.PYSON) {
|
|
if (jQuery(value.types()).not(
|
|
['number', typeof null]).length) {
|
|
throw name + ' must be an integer or None';
|
|
}
|
|
} else {
|
|
if ((typeof value != 'number') && (value !== null)) {
|
|
throw name + ' must be an integer or None';
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.Date.eval_ = function(value, context) {
|
|
var date = Sao.Date();
|
|
date.setHours(0);
|
|
date.setMinutes(0);
|
|
date.setSeconds(0);
|
|
date.setMilliseconds(0);
|
|
if (value.y) date.setFullYear(value.y);
|
|
if (value.M) date.setMonth(value.M - 1);
|
|
if (value.d) date.setDate(value.d);
|
|
var year = date.getFullYear();
|
|
var month = date.getMonth();
|
|
var day = date.getDate();
|
|
if (value.dy) date.setFullYear(year + value.dy);
|
|
if (value.dM) date.setMonth(month + value.dM);
|
|
if (value.dd) date.setDate(day + value.dd);
|
|
return date;
|
|
};
|
|
|
|
Sao.PYSON.DateTime = Sao.class_(Sao.PYSON.Date, {
|
|
init: function(year, month, day, hour, minute, second, microsecond,
|
|
delta_years, delta_months, delta_days, delta_hours,
|
|
delta_minutes, delta_seconds, delta_microseconds) {
|
|
Sao.PYSON.DateTime._super.init.call(this, year, month, day,
|
|
delta_years, delta_months, delta_days);
|
|
if (hour === undefined) hour = null;
|
|
if (minute === undefined) minute = null;
|
|
if (second === undefined) second = null;
|
|
if (microsecond === undefined) microsecond = null;
|
|
if (delta_hours === undefined) delta_hours = 0;
|
|
if (delta_minutes === undefined) delta_minutes = 0;
|
|
if (delta_seconds === undefined) delta_seconds = 0;
|
|
if (delta_microseconds === undefined) delta_microseconds = 0;
|
|
|
|
this._test(hour, 'hour');
|
|
this._test(minute, 'minute');
|
|
this._test(second, 'second');
|
|
this._test(microsecond, 'microsecond');
|
|
this._test(delta_hours, 'delta_hours');
|
|
this._test(delta_minutes, 'delta_minutes');
|
|
this._test(delta_seconds, 'delta_seconds');
|
|
this._test(delta_microseconds, 'delta_microseconds');
|
|
|
|
this._hour = hour;
|
|
this._minute = minute;
|
|
this._second = second;
|
|
this._microsecond = microsecond;
|
|
this._delta_hours = delta_hours;
|
|
this._delta_minutes = delta_minutes;
|
|
this._delta_seconds = delta_seconds;
|
|
this._delta_microseconds = delta_microseconds;
|
|
},
|
|
pyson: function() {
|
|
var result = Sao.PYSON.DateTime._super.pyson.call(this);
|
|
result.__class__ = 'DateTime';
|
|
result.h = this._hour;
|
|
result.m = this._minute;
|
|
result.s = this._second;
|
|
result.ms = this._microsecond;
|
|
result.dh = this._delta_hours;
|
|
result.dm = this._delta_minutes;
|
|
result.ds = this._delta_seconds;
|
|
result.dms = this._delta_microseconds;
|
|
return result;
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.DateTime.eval_ = function(value, context) {
|
|
var date = Sao.DateTime();
|
|
if (value.y) date.setFullYear(value.y);
|
|
if (value.M) date.setMonth(value.M - 1);
|
|
if (value.d) date.setDate(value.d);
|
|
if (value.h !== undefined) date.setHours(value.h);
|
|
if (value.m !== undefined) date.setMinutes(value.m);
|
|
if (value.s !== undefined) date.setSeconds(value.s);
|
|
if (value.ms !== undefined) date.setMilliseconds(value.ms / 100);
|
|
var year = date.getFullYear();
|
|
var month = date.getMonth();
|
|
var day = date.getDate();
|
|
var hour = date.getHours();
|
|
var minute = date.getMinutes();
|
|
var second = date.getSeconds();
|
|
var millisecond = date.getMilliseconds();
|
|
if (value.dy) date.setFullYear(year + value.dy);
|
|
if (value.dM) date.setMonth(month + value.dM);
|
|
if (value.dd) date.setDate(day + value.dd);
|
|
if (value.dh) date.setHours(hour + value.dh);
|
|
if (value.dm) date.setMinutes(minute + value.dm);
|
|
if (value.ds) date.setSeconds(second + value.ds);
|
|
if (value.dms) date.setMilliseconds(millisecond + value.dms / 100);
|
|
return date;
|
|
};
|
|
|
|
Sao.PYSON.Len = Sao.class_(Sao.PYSON.PYSON, {
|
|
init: function(value) {
|
|
Sao.PYSON.Len._super.init.call(this);
|
|
if (value instanceof Sao.PYSON.PYSON) {
|
|
if (jQuery(value.types()).not(['object', 'string']).length ||
|
|
jQuery(['object', 'string']).not(value.types()).length) {
|
|
throw 'value must be an object or a string';
|
|
}
|
|
} else {
|
|
if ((typeof value != 'object') && (typeof value != 'string')) {
|
|
throw 'value must be an object or a string';
|
|
}
|
|
}
|
|
this._value = value;
|
|
},
|
|
pyson: function() {
|
|
return {
|
|
'__class__': 'Len',
|
|
'v': this._value
|
|
};
|
|
},
|
|
types: function() {
|
|
return ['integer'];
|
|
}
|
|
});
|
|
|
|
Sao.PYSON.Len.eval_ = function(value, context) {
|
|
if (typeof value.v == 'object') {
|
|
return Object.keys(value.v).length;
|
|
} else {
|
|
return value.v.length;
|
|
}
|
|
};
|
|
}());
|