bzr revid: nicolas.vanhoren@openerp.com-20120227132024-06p0skdlx1ixbi3g
This commit is contained in:
niv-openerp 2012-02-27 14:20:24 +01:00
commit d1498035f0
24 changed files with 13580 additions and 3168 deletions

View File

@ -15,7 +15,8 @@
"static/lib/datejs/parser.js",
"static/lib/datejs/sugarpak.js",
"static/lib/datejs/extras.js",
"static/lib/jquery/jquery-1.6.4.js",
#"static/lib/jquery/jquery-1.6.4.js",
"static/lib/jquery/jquery-1.7.2b1.js",
"static/lib/jquery.MD5/jquery.md5.js",
"static/lib/jquery.form/jquery.form.js",
"static/lib/jquery.validate/jquery.validate.js",
@ -35,7 +36,7 @@
"static/lib/underscore/underscore.js",
"static/lib/underscore/underscore.string.js",
"static/lib/labjs/LAB.src.js",
"static/lib/py.parse/lib/py.js",
"static/lib/py.js/lib/py.js",
"static/lib/novajs/src/nova.js",
"static/src/js/boot.js",
"static/src/js/core.js",
@ -60,6 +61,7 @@
"static/lib/jquery.ui.timepicker/css/jquery-ui-timepicker-addon.css",
"static/lib/jquery.ui.notify/css/ui.notify.css",
"static/lib/jquery.tipsy/tipsy.css",
"static/src/css/base_old.css",
"static/src/css/base.css",
"static/src/css/data_export.css",
"static/src/css/data_import.css",

View File

@ -113,6 +113,57 @@ html_template = """<!DOCTYPE html>
</html>
"""
def sass2scss(src):
# Validated by diff -u of sass2scss against:
# sass-convert -F sass -T scss openerp.sass openerp.scss
block = []
sass = ('', block)
reComment = re.compile(r'//.*$')
reIndent = re.compile(r'^\s+')
reIgnore = re.compile(r'^\s*(//.*)?$')
reFixes = { re.compile(r'\(\((.*)\)\)') : r'(\1)', }
lastLevel = 0
prevBlocks = {}
for l in src.split('\n'):
l = l.rstrip()
if reIgnore.search(l): continue
l = reComment.sub('', l)
l = l.rstrip()
indent = reIndent.match(l)
level = indent.end() if indent else 0
l = l[level:]
if level>lastLevel:
prevBlocks[lastLevel] = block
newBlock = []
block[-1] = (block[-1], newBlock)
block = newBlock
elif level<lastLevel:
block = prevBlocks[level]
lastLevel = level
if not l: continue
# Fixes
for ereg, repl in reFixes.items():
l = ereg.sub(repl if type(repl)==str else repl(), l)
block.append(l)
def write(sass, level=-1):
out = ""
indent = ' '*level
if type(sass)==tuple:
if level>=0:
out += indent+sass[0]+" {\n"
for e in sass[1]:
out += write(e, level+1)
if level>=0:
out = out.rstrip(" \n")
out += ' }\n'
if level==0:
out += "\n"
else:
out += indent+sass+";\n"
return out
return write(sass)
class WebClient(openerpweb.Controller):
_cp_path = "/web/webclient"

View File

@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
"PO-Revision-Date: 2012-02-09 20:50+0000\n"
"PO-Revision-Date: 2012-02-24 11:29+0000\n"
"Last-Translator: Ahmet Altınışık <Unknown>\n"
"Language-Team: Turkish <tr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
"X-Generator: Launchpad (build 14781)\n"
"X-Launchpad-Export-Date: 2012-02-25 05:30+0000\n"
"X-Generator: Launchpad (build 14860)\n"
#. openerp-web
#: addons/web/static/src/js/chrome.js:172
@ -192,7 +192,7 @@ msgstr "Karşıdan yükle \"%s\""
#. openerp-web
#: addons/web/static/src/js/search.js:191
msgid "Filter disabled due to invalid syntax"
msgstr ""
msgstr "Geçersiz sözdizimi nedeniyle filtre engellendi"
#. openerp-web
#: addons/web/static/src/js/search.js:237
@ -381,12 +381,12 @@ msgstr "Editörü göster %d - %s"
#. openerp-web
#: addons/web/static/src/js/view_editor.js:367
msgid "Inherited View"
msgstr ""
msgstr "Devralınan Görünüm"
#. openerp-web
#: addons/web/static/src/js/view_editor.js:371
msgid "Do you really wants to create an inherited view here?"
msgstr ""
msgstr "Devralınmış görünüm oluşturmak istediğinden emin misin?"
#. openerp-web
#: addons/web/static/src/js/view_editor.js:381

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea
node: a2f78eecb94af8e38839e47b3a5b2ca21f1ed6c3
branch: default
tag: 0.4

View File

@ -0,0 +1,85 @@
What
====
``py.js`` is a parser and evaluator of Python expressions, written in
pure javascript.
``py.js`` is not intended to implement a full Python interpreter
(although it could be used for such an effort later on), its
specification document is the `Python 2.7 Expressions spec
<http://docs.python.org/reference/expressions.html>`_ (along with the
lexical analysis part).
Why
===
Originally, to learn about Pratt parsers (which are very, very good at
parsing expressions with lots of infix or mixfix symbols). The
evaluator part came because "why not" and because I work on a product
with the "feature" of transmitting Python expressions (over the wire)
which the client is supposed to evaluate.
How
===
At this point, only three steps exist in ``py.js``: tokenizing,
parsing and evaluation. It is possible that a compilation step be
added later (for performance reasons).
To evaluate a Python expression, the caller merely needs to call
`py.eval`_. `py.eval`_ takes a mandatory Python
expression to evaluate (as a string) and an optional context, for the
substitution of the free variables in the expression::
> py.eval("type in ('a', 'b', 'c') and foo", {type: 'c', foo: true});
true
This is great for one-shot evaluation of expressions. If the
expression will need to be repeatedly evaluated with the same
parameters, the various parsing and evaluation steps can be performed
separately: `py.eval`_ is really a shortcut for sequentially calling
`py.tokenize`_, `py.parse`_ and `py.evaluate`_.
API
===
.. _py.eval:
``py.eval(expr[, context])``
"Do everything" function, to use for one-shot evaluation of a
Python expression: it will internally handle the tokenizing,
parsing and actual evaluation of the Python expression without
having to perform these separately.
``expr``
Python expression to evaluate
``context``
context dictionary holding the substitutions for the free
variables in the expression
.. _py.tokenize:
``py.tokenize(expr)``
``expr``
Python expression to tokenize
.. _py.parse:
``py.parse(tokens)``
Parses a token stream and returns an abstract syntax tree of the
expression (if the token stream represents a valid Python
expression).
A parse tree is stateless and can be memoized and used multiple
times in separate evaluations.
``tokens``
stream of tokens returned by `py.tokenize`_
.. _py.evaluate:
``py.evaluate(ast[, context])``
``ast``
The output of `py.parse`_
``context``
The evaluation context for the Python expression.

View File

@ -0,0 +1,48 @@
* Parser
since parsing expressions, try with a pratt parser
http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
http://effbot.org/zone/simple-top-down-parsing.htm
Evaluator
---------
* Builtins should be built-in, there should be no need to add e.g. ``py.bool`` to the evaluation context (?)
* Stop busyworking trivial binary operator
* Make it *trivial* to build Python type-wrappers
* Implement Python's `data model protocols
<http://docs.python.org/reference/datamodel.html#basic-customization>`_
for *all* supported operations, optimizations can come later
* Automatically type-wrap everything (for now anyway)
Base type requirements:
***********************
* int
* float
* --str-- unicode
* bool
* dict
* tuple
* list
* ?module
* ?object
* datetime.time
* datetime.timedelta
* NotImplementedType
Base methods requirement
************************
* ``__getattr__``
* ? ``__getitem``
* ``__call__``
* ``or``
* ``toJS`` / ``toJSON``
* ``dict.get``
* ``datetime.time.today``
* ``datetime.time.strftime``
* ``time.strftime``
* ``__add__`` / ``__radd__``
* ``__sub__`` / ``__rsub__``
* ``__len__``
* ``__nonzero__``

View File

@ -0,0 +1,750 @@
var py = {};
(function (py) {
var NUMBER = /^\d$/,
NAME_FIRST = /^[a-zA-Z_]$/,
NAME = /^[a-zA-Z0-9_]$/;
var create = function (o, props) {
function F() {}
F.prototype = o;
var inst = new F;
if (props) {
for(var name in props) {
if(!props.hasOwnProperty(name)) { continue; }
inst[name] = props[name];
}
}
return inst;
};
var symbols = {};
var comparators = {};
var Base = {
nud: function () { throw new Error(this.id + " undefined as prefix"); },
led: function (led) { throw new Error(this.id + " undefined as infix"); },
toString: function () {
if (this.id === '(constant)' || this.id === '(number)' || this.id === '(name)' || this.id === '(string)') {
return [this.id.slice(0, this.id.length-1), ' ', this.value, ')'].join('');
} else if (this.id === '(end)') {
return '(end)';
} else if (this.id === '(comparator)' ) {
var repr = ['(comparator', this.expressions[0]];
for (var i=0;i<this.operators.length; ++i) {
repr.push(this.operators[i], this.expressions[i+1]);
}
return repr.join(' ') + ')';
}
var out = [this.id, this.first, this.second, this.third]
.filter(function (r){return r}).join(' ');
return '(' + out + ')';
}
};
function symbol(id, bp) {
bp = bp || 0;
var s = symbols[id];
if (s) {
if (bp > s.lbp) {
s.lbp = bp;
}
return s;
}
return symbols[id] = create(Base, {
id: id,
lbp: bp
});
}
function constant(id) {
var s = symbol(id);
s.id = '(constant)';
s.value = id;
s.nud = function () {
return this;
};
}
function prefix(id, bp, nud) {
symbol(id).nud = nud || function () {
this.first = expression(bp);
return this
}
}
function infix(id, bp, led) {
symbol(id, bp).led = led || function (left) {
this.first = left;
this.second = expression(bp);
return this;
}
}
function infixr(id, bp) {
symbol(id, bp).led = function (left) {
this.first = left;
this.second = expression(bp - 1);
return this;
}
}
function comparator(id) {
comparators[id] = true;
var bp = 60;
infix(id, bp, function (left) {
this.id = '(comparator)';
this.operators = [id];
this.expressions = [left, expression(bp)];
while (token.id in comparators) {
this.operators.push(token.id);
advance();
this.expressions.push(
expression(bp));
}
return this;
});
}
constant('None'); constant('False'); constant('True');
symbol('(number)').nud = function () { return this; };
symbol('(name)').nud = function () { return this; };
symbol('(string)').nud = function () { return this; };
symbol('(end)');
symbol(':'); symbol(')'); symbol(']'); symbol('}'); symbol(',');
symbol('else');
symbol('lambda', 20).nud = function () {
this.first = [];
if (token.id !== ':') {
for(;;) {
if (token.id !== '(name)') {
throw new Error('Excepted an argument name');
}
this.first.push(token);
advance();
if (token.id !== ',') {
break;
}
advance(',');
}
}
advance(':');
this.second = expression();
return this;
};
infix('if', 20, function (left) {
this.first = left;
this.second = expression();
advance('else');
this.third = expression();
return this;
});
infixr('or', 30); infixr('and', 40); prefix('not', 50);
comparator('in'); comparator('not in');
comparator('is'); comparator('is not');
comparator('<'); comparator('<=');
comparator('>'); comparator('>=');
comparator('<>'); comparator('!='); comparator('==');
infix('|', 70); infix('^', 80), infix('&', 90);
infix('<<', 100); infix('>>', 100);
infix('+', 110); infix('-', 110);
infix('*', 120); infix('/', 120);
infix('//', 120), infix('%', 120);
prefix('-', 130); prefix('+', 130); prefix('~', 130);
infixr('**', 140);
infix('.', 150, function (left) {
if (token.id !== '(name)') {
throw new Error('Expected attribute name, got ' + token.id);
}
this.first = left;
this.second = token;
advance();
return this;
});
symbol('(', 150).nud = function () {
this.first = [];
var comma = false;
if (token.id !== ')') {
while (true) {
if (token.id === ')') {
break;
}
this.first.push(expression());
if (token.id !== ',') {
break;
}
comma = true;
advance(',');
}
}
advance(')');
if (!this.first.length || comma) {
return this;
} else {
return this.first[0];
}
};
symbol('(').led = function (left) {
this.first = left;
this.second = [];
if (token.id !== ")") {
for(;;) {
this.second.push(expression());
if (token.id !== ',') {
break;
}
advance(',');
}
}
advance(")");
return this;
};
infix('[', 150, function (left) {
this.first = left;
this.second = expression();
advance("]");
return this;
});
symbol('[').nud = function () {
this.first = [];
if (token.id !== ']') {
for (;;) {
if (token.id === ']') {
break;
}
this.first.push(expression());
if (token.id !== ',') {
break;
}
advance(',');
}
}
advance(']');
return this;
};
symbol('{').nud = function () {
this.first = [];
if (token.id !== '}') {
for(;;) {
if (token.id === '}') {
break;
}
var key = expression();
advance(':');
var value = expression();
this.first.push([key, value]);
if (token.id !== ',') {
break;
}
advance(',');
}
}
advance('}');
return this;
};
py.tokenize = (function () {
function group() { return '(' + Array.prototype.join.call(arguments, '|') + ')'; }
var Whitespace = '[ \\f\\t]*';
var Name = '[a-zA-Z_]\\w*';
var DecNumber = '\\d+'
var IntNumber = DecNumber;
var PointFloat = group('\\d+\\.\\d*', '\\.\\d+');
var FloatNumber = PointFloat;
var Number = group(FloatNumber, IntNumber);
var Operator = group("\\*\\*=?", ">>=?", "<<=?", "<>", "!=",
"//=?", "[+\\-*/%&|^=<>]=?", "~");
var Bracket = '[\\[\\]\\(\\)\\{\\}]';
var Special = '[:;.,`@]';
var Funny = group(Operator, Bracket, Special)
var ContStr = group("'[^']*'", '"[^"]*"');
var PseudoToken = Whitespace + group(Number, Funny, ContStr, Name);
return function tokenize(s) {
var max=s.length, tokens = [];
// /g flag makes repeated exec() have memory
var pseudoprog = new RegExp(PseudoToken, 'g');
while(pseudoprog.lastIndex < max) {
var pseudomatch = pseudoprog.exec(s);
if (!pseudomatch) {
throw new Error('Failed to tokenize ' + s
+ ' at index ' + (end || 0)
+ '; parsed so far: ' + tokens);
}
var start = pseudomatch.index, end = pseudoprog.lastIndex;
// strip leading space caught by Whitespace
var token = s.slice(start, end).replace(new RegExp('^' + Whitespace), '');
var initial = token[0];
if (/\d/.test(initial) || (initial === '.' && token !== '.')) {
tokens.push(create(symbols['(number)'], {
value: parseFloat(token)
}));
} else if (/'|"/.test(initial)) {
tokens.push(create(symbols['(string)'], {
value: token.slice(1, -1)
}));
} else if (token in symbols) {
var symbol;
// transform 'not in' and 'is not' in a single token
if (token === 'in' && tokens[tokens.length-1].id === 'not') {
symbol = symbols['not in'];
tokens.pop();
} else if (token === 'not' && tokens[tokens.length-1].id === 'is') {
symbol = symbols['is not'];
tokens.pop();
} else {
symbol = symbols[token];
}
tokens.push(create(symbol));
} else if (/[_a-zA-Z]/.test(initial)) {
tokens.push(create(symbols['(name)'], {
value: token
}));
} else {
throw new Error("Tokenizing failure of <<" + s + ">> at index " + start
+ " for token [[" + token + "]]"
+ "; parsed so far: " + tokens);
}
}
tokens.push(create(symbols['(end)']));
return tokens;
}
})()
var token, next;
function expression(rbp) {
rbp = rbp || 0;
var t = token;
token = next();
var left = t.nud();
while (rbp < token.lbp) {
t = token;
token = next();
left = t.led(left);
}
return left;
}
function advance(id) {
if (id && token.id !== id) {
throw new Error(
'Expected "' + id + '", got "' + token.id + '"');
}
token = next();
}
function PY_ensurepy(val, name) {
switch (val) {
case undefined:
throw new Error("NameError: name '" + name + "' is not defined");
case null:
return py.None;
case true:
return py.True;
case false:
return py.False;
}
if (val instanceof py.object
|| val === py.object
|| py.issubclass.__call__(val, py.object) === py.True) {
return val;
}
switch (typeof val) {
case 'number':
return new py.float(val);
case 'string':
return new py.str(val);
case 'function':
return new py.def(val);
}
throw new Error("Could not convert " + val + " to a pyval");
}
// Builtins
py.type = function type(constructor, base, dict) {
var proto;
if (!base) {
base = py.object;
}
proto = constructor.prototype = create(base.prototype);
if (dict) {
for(var k in dict) {
if (!dict.hasOwnProperty(k)) { continue; }
proto[k] = dict[k];
}
}
constructor.__call__ = function () {
// create equivalent type with same prototype
var instance = create(proto);
// call actual constructor
var res = constructor.apply(instance, arguments);
// return result of constructor if any, otherwise instance
return res || instance;
}
return constructor;
}
var hash_counter = 0;
py.object = py.type(function object() {}, {}, {
// Basic customization
__hash__: function () {
if (this._hash) {
return this._hash;
}
return this._hash = hash_counter++;
},
__eq__: function (other) {
return (this === other) ? py.True : py.False;
},
__ne__: function (other) {
if (this.__eq__(other) === py.True) {
return py.False;
} else {
return py.True;
}
},
__str__: function () {
return this.__unicode__();
},
__unicode__: function () {
// TODO: return python string
return '<object ' + this.constructor.name + '>';
},
__nonzero__: function () {
return py.True;
},
// Attribute access
__getattribute__: function (name) {
if (name in this) {
var val = this[name];
if ('__get__' in val) {
// TODO: second argument should be class
return val.__get__(this);
}
if (typeof val === 'function' && !this.hasOwnProperty(val)) {
// val is a method from the class
return new PY_instancemethod(val, this);
}
return PY_ensurepy(val);
}
if ('__getattr__' in this) {
return this.__getattr__(name);
}
throw new Error("AttributeError: object has no attribute '" + name +"'");
},
__setattr__: function (name, value) {
if (name in this && '__set__' in this[name]) {
this[name].__set__(this, value);
}
this[name] = value;
},
// no delattr, because no 'del' statement
// Conversion
toJSON: function () {
throw new Error(this.constructor.name + ' can not be converted to JSON');
}
});
NoneType = py.type(function () {}, py.object, {
__nonzero__: function () { return py.False; },
toJSON: function () { return null; }
});
py.None = new NoneType();
var booleans_initialized = false;
py.bool = py.type(function bool(value) {
// The only actual instance of py.bool should be py.True
// and py.False. Return the new instance of py.bool if we
// are initializing py.True and py.False, otherwise always
// return either py.True or py.False.
if (!booleans_initialized) {
return;
}
if (value === undefined) { return py.False; }
return value.__nonzero__() === py.True ? py.True : py.False;
}, py.object, {
__nonzero__: function () { return this; },
toJSON: function () { return this === py.True; }
});
py.True = new py.bool();
py.False = new py.bool();
booleans_initialized = true;
py.float = py.type(function float(value) {
this._value = value;
}, py.object, {
__eq__: function (other) {
return this._value === other._value ? py.True : py.False;
},
__lt__: function (other) {
return this._value < other._value ? py.True : py.False;
},
__le__: function (other) {
return this._value <= other._value ? py.True : py.False;
},
__gt__: function (other) {
return this._value > other._value ? py.True : py.False;
},
__ge__: function (other) {
return this._value >= other._value ? py.True : py.False;
},
__neg__: function () {
return new py.float(-this._value);
},
__nonzero__: function () {
return this._value ? py.True : py.False;
},
toJSON: function () {
return this._value;
}
});
py.str = py.type(function str(s) {
this._value = s;
}, py.object, {
__eq__: function (other) {
if (other instanceof py.str && this._value === other._value) {
return py.True;
}
return py.False;
},
__lt__: function (other) {
return this._value < other._value ? py.True : py.False;
},
__le__: function (other) {
return this._value <= other._value ? py.True : py.False;
},
__gt__: function (other) {
return this._value > other._value ? py.True : py.False;
},
__ge__: function (other) {
return this._value >= other._value ? py.True : py.False;
},
__nonzero__: function () {
return this._value.length ? py.True : py.False;
},
__contains__: function (s) {
return (this._value.indexOf(s._value) !== -1) ? py.True : py.False;
},
toJSON: function () {
return this._value;
}
});
py.tuple = py.type(function tuple() {}, null, {
__contains__: function (value) {
for(var i=0, len=this.values.length; i<len; ++i) {
if (this.values[i].__eq__(value) === py.True) {
return py.True;
}
}
return py.False;
},
toJSON: function () {
var out = [];
for (var i=0; i<this.values.length; ++i) {
out.push(this.values[i].toJSON());
}
return out;
}
});
py.list = py.tuple;
py.dict = py.type(function dict() {
this._store = {};
}, py.object, {
__setitem__: function (key, value) {
this._store[key.__hash__()] = [key, value];
},
toJSON: function () {
var out = {};
for(var k in this._store) {
var item = this._store[k];
out[item[0].toJSON()] = item[1].toJSON();
}
return out;
}
});
py.def = py.type(function def(nativefunc) {
this._inst = null;
this._func = nativefunc;
}, py.object, {
__call__: function () {
// don't want to rewrite __call__ for instancemethod
return this._func.apply(this._inst, arguments);
},
toJSON: function () {
return this._func;
}
});
var PY_instancemethod = py.type(function instancemethod(nativefunc, instance, _cls) {
// could also use bind?
this._inst = instance;
this._func = nativefunc;
}, py.def, {});
py.issubclass = new py.def(function issubclass(derived, parent) {
// still hurts my brain that this can work
return derived.prototype instanceof py.object
? py.True
: py.False;
});
var PY_builtins = {
type: py.type,
None: py.None,
True: py.True,
False: py.False,
object: py.object,
bool: py.bool,
float: py.float,
tuple: py.tuple,
list: py.list,
dict: py.dict,
issubclass: py.issubclass
};
py.parse = function (toks) {
var index = 0;
token = toks[0];
next = function () { return toks[++index]; };
return expression();
};
var evaluate_operator = function (operator, a, b) {
switch (operator) {
case '==': return a.__eq__(b)
case 'is': return a === b ? py.True : py.False;
case '!=': return a.__ne__(b)
case 'is not': return a !== b ? py.True : py.False;
case '<': return a.__lt__(b);
case '<=': return a.__le__(b);
case '>': return a.__gt__(b);
case '>=': return a.__ge__(b);
case 'in':
return b.__contains__(a);
case 'not in':
return b.__contains__(a) === py.True ? py.False : py.True;
}
throw new Error('SyntaxError: unknown comparator [[' + operator + ']]');
};
py.evaluate = function (expr, context) {
context = context || {};
switch (expr.id) {
case '(name)':
var val = context[expr.value];
if (val === undefined && expr.value in PY_builtins) {
return PY_builtins[expr.value];
}
return PY_ensurepy(val, expr.value);
case '(string)':
return new py.str(expr.value);
case '(number)':
return new py.float(expr.value);
case '(constant)':
switch (expr.value) {
case 'None': return py.None;
case 'False': return py.False;
case 'True': return py.True;
}
throw new Error("SyntaxError: unknown constant '" + expr.value + "'");
case '(comparator)':
var result, left = py.evaluate(expr.expressions[0], context);
for(var i=0; i<expr.operators.length; ++i) {
result = evaluate_operator(
expr.operators[i],
left,
left = py.evaluate(expr.expressions[i+1], context));
if (result === py.False) { return py.False; }
}
return py.True;
case '-':
if (expr.second) {
throw new Error('SyntaxError: binary [-] not implemented yet');
}
return (py.evaluate(expr.first, context)).__neg__();
case 'not':
return py.evaluate(expr.first, context).__nonzero__() === py.True
? py.False
: py.True;
case 'and':
var and_first = py.evaluate(expr.first, context);
if (and_first.__nonzero__() === py.True) {
return py.evaluate(expr.second, context);
}
return and_first;
case 'or':
var or_first = py.evaluate(expr.first, context);
if (or_first.__nonzero__() === py.True) {
return or_first
}
return py.evaluate(expr.second, context);
case '(':
if (expr.second) {
var callable = py.evaluate(expr.first, context), args=[];
for (var jj=0; jj<expr.second.length; ++jj) {
args.push(py.evaluate(
expr.second[jj], context));
}
return callable.__call__.apply(callable, args);
}
var tuple_exprs = expr.first,
tuple_values = [];
for (var j=0, len=tuple_exprs.length; j<len; ++j) {
tuple_values.push(py.evaluate(
tuple_exprs[j], context));
}
var t = new py.tuple();
t.values = tuple_values;
return t;
case '[':
if (expr.second) {
throw new Error('SyntaxError: indexing not implemented yet');
}
var list_exprs = expr.first, list_values = [];
for (var k=0; k<list_exprs.length; ++k) {
list_values.push(py.evaluate(
list_exprs[k], context));
}
var l = new py.list();
l.values = list_values;
return l;
case '{':
var dict_exprs = expr.first, dict = new py.dict;
for(var l=0; l<dict_exprs.length; ++l) {
dict.__setitem__(
py.evaluate(dict_exprs[l][0], context),
py.evaluate(dict_exprs[l][1], context));
}
return dict;
case '.':
if (expr.second.id !== '(name)') {
throw new Error('SyntaxError: ' + expr);
}
return py.evaluate(expr.first, context)
.__getattribute__(expr.second.value);
default:
throw new Error('SyntaxError: Unknown node [[' + expr.id + ']]');
}
};
py.eval = function (str, context) {
return py.evaluate(
py.parse(
py.tokenize(
str)),
context).toJSON();
}
})(typeof exports === 'undefined' ? py : exports);

View File

@ -0,0 +1,96 @@
var py = require('../lib/py.js'),
expect = require('expect.js');
expect.Assertion.prototype.tokens = function (n) {
var length = this.obj.length;
this.assert(length === n + 1,
'expected ' + this.obj + ' to have ' + n + ' tokens',
'expected ' + this.obj + ' to not have ' + n + ' tokens');
this.assert(this.obj[length-1].id === '(end)',
'expected ' + this.obj + ' to have and end token',
'expected ' + this.obj + ' to not have an end token');
};
expect.Assertion.prototype.constant = function (value) {
this.assert(this.obj.id === '(constant)',
'expected ' + this.obj + ' to be a constant token',
'expected ' + this.obj + ' not to be a constant token');
this.assert(this.obj.value === value,
'expected ' + this.obj + ' to have tokenized ' + value,
'expected ' + this.obj + ' not to have tokenized ' + value);
};
expect.Assertion.prototype.number = function (value) {
this.assert(this.obj.id === '(number)',
'expected ' + this.obj + ' to be a number token',
'expected ' + this.obj + ' not to be a number token');
this.assert(this.obj.value === value,
'expected ' + this.obj + ' to have tokenized ' + value,
'expected ' + this.obj + ' not to have tokenized ' + value);
};
expect.Assertion.prototype.string = function (value) {
this.assert(this.obj.id === '(string)',
'expected ' + this.obj + ' to be a string token',
'expected ' + this.obj + ' not to be a string token');
this.assert(this.obj.value === value,
'expected ' + this.obj + ' to have tokenized ' + value,
'expected ' + this.obj + ' not to have tokenized ' + value);
};
describe('Tokenizer', function () {
describe('simple literals', function () {
it('tokenizes numbers', function () {
var toks = py.tokenize('1');
expect(toks).to.have.tokens(1);
expect(toks[0]).to.be.number(1);
var toks = py.tokenize('-1');
expect(toks).to.have.tokens(2);
expect(toks[0].id).to.be('-');
expect(toks[1]).to.be.number(1);
var toks = py.tokenize('1.2');
expect(toks).to.have.tokens(1);
expect(toks[0]).to.be.number(1.2);
var toks = py.tokenize('.42');
expect(toks).to.have.tokens(1);
expect(toks[0]).to.be.number(0.42);
});
it('tokenizes strings', function () {
var toks = py.tokenize('"foo"');
expect(toks).to.have.tokens(1);
expect(toks[0]).to.be.string('foo');
var toks = py.tokenize("'foo'");
expect(toks).to.have.tokens(1);
expect(toks[0]).to.be.string('foo');
});
it('tokenizes bare names', function () {
var toks = py.tokenize('foo');
expect(toks).to.have.tokens(1);
expect(toks[0].id).to.be('(name)');
expect(toks[0].value).to.be('foo');
});
it('tokenizes constants', function () {
var toks = py.tokenize('None');
expect(toks).to.have.tokens(1);
expect(toks[0]).to.be.constant('None');
var toks = py.tokenize('True');
expect(toks).to.have.tokens(1);
expect(toks[0]).to.be.constant('True');
var toks = py.tokenize('False');
expect(toks).to.have.tokens(1);
expect(toks[0]).to.be.constant('False');
});
});
describe('collections', function () {
it('tokenizes opening and closing symbols', function () {
var toks = py.tokenize('()');
expect(toks).to.have.tokens(2);
expect(toks[0].id).to.be('(');
expect(toks[1].id).to.be(')');
});
});
});

View File

@ -0,0 +1,283 @@
var py = require('../lib/py.js'),
expect = require('expect.js');
var ev = function (str, context) {
return py.evaluate(py.parse(py.tokenize(str)), context);
};
describe('Literals', function () {
describe('Number', function () {
it('should have the right type', function () {
expect(ev('1')).to.be.a(py.float);
});
it('should yield the corresponding JS value', function () {
expect(py.eval('1')).to.be(1);
expect(py.eval('42')).to.be(42);
expect(py.eval('9999')).to.be(9999);
});
it('should correctly handle negative literals', function () {
expect(py.eval('-1')).to.be(-1);
expect(py.eval('-42')).to.be(-42);
expect(py.eval('-9999')).to.be(-9999);
});
it('should correctly handle float literals', function () {
expect(py.eval('.42')).to.be(0.42);
expect(py.eval('1.2')).to.be(1.2);
});
});
describe('Booleans', function () {
it('should have the right type', function () {
expect(ev('False')).to.be.a(py.bool);
expect(ev('True')).to.be.a(py.bool);
});
it('should yield the corresponding JS value', function () {
expect(py.eval('False')).to.be(false);
expect(py.eval('True')).to.be(true);
});
});
describe('None', function () {
it('should have the right type', function () {
expect(ev('None')).to.be.a(py.object)
});
it('should yield a JS null', function () {
expect(py.eval('None')).to.be(null);
});
});
describe('String', function () {
it('should have the right type', function () {
expect(ev('"foo"')).to.be.a(py.str);
expect(ev("'foo'")).to.be.a(py.str);
});
it('should yield the corresponding JS string', function () {
expect(py.eval('"somestring"')).to.be('somestring');
expect(py.eval("'somestring'")).to.be('somestring');
});
});
describe('Tuple', function () {
it('shoud have the right type', function () {
expect(ev('()')).to.be.a(py.tuple);
});
it('should map to a JS array', function () {
expect(py.eval('()')).to.eql([]);
expect(py.eval('(1, 2, 3)')).to.eql([1, 2, 3]);
});
});
describe('List', function () {
it('shoud have the right type', function () {
expect(ev('[]')).to.be.a(py.list);
});
it('should map to a JS array', function () {
expect(py.eval('[]')).to.eql([]);
expect(py.eval('[1, 2, 3]')).to.eql([1, 2, 3]);
});
});
describe('Dict', function () {
it('shoud have the right type', function () {
expect(ev('{}')).to.be.a(py.dict);
});
it('should map to a JS object', function () {
expect(py.eval("{}")).to.eql({});
expect(py.eval("{'foo': 1, 'bar': 2}"))
.to.eql({foo: 1, bar: 2});
});
});
});
describe('Free variables', function () {
it('should return its identity', function () {
expect(py.eval('foo', {foo: 1})).to.be(1);
expect(py.eval('foo', {foo: true})).to.be(true);
expect(py.eval('foo', {foo: false})).to.be(false);
expect(py.eval('foo', {foo: null})).to.be(null);
expect(py.eval('foo', {foo: 'bar'})).to.be('bar');
});
});
describe('Comparisons', function () {
describe('equality', function () {
it('should work with literals', function () {
expect(py.eval('1 == 1')).to.be(true);
expect(py.eval('"foo" == "foo"')).to.be(true);
expect(py.eval('"foo" == "bar"')).to.be(false);
});
it('should work with free variables', function () {
expect(py.eval('1 == a', {a: 1})).to.be(true);
expect(py.eval('foo == "bar"', {foo: 'bar'})).to.be(true);
expect(py.eval('foo == "bar"', {foo: 'qux'})).to.be(false);
});
});
describe('inequality', function () {
it('should work with literals', function () {
expect(py.eval('1 != 2')).to.be(true);
expect(py.eval('"foo" != "foo"')).to.be(false);
expect(py.eval('"foo" != "bar"')).to.be(true);
});
it('should work with free variables', function () {
expect(py.eval('1 != a', {a: 42})).to.be(true);
expect(py.eval('foo != "bar"', {foo: 'bar'})).to.be(false);
expect(py.eval('foo != "bar"', {foo: 'qux'})).to.be(true);
expect(py.eval('foo != bar', {foo: 'qux', bar: 'quux'}))
.to.be(true);
});
});
describe('rich comparisons', function () {
it('should work with numbers', function () {
expect(py.eval('3 < 5')).to.be(true);
expect(py.eval('5 >= 3')).to.be(true);
expect(py.eval('3 >= 3')).to.be(true);
expect(py.eval('3 > 5')).to.be(false);
});
it('should support comparison chains', function () {
expect(py.eval('1 < 3 < 5')).to.be(true);
expect(py.eval('5 > 3 > 1')).to.be(true);
expect(py.eval('1 < 3 > 2 == 2 > -2')).to.be(true);
});
it('should compare strings', function () {
expect(py.eval('date >= current',
{date: '2010-06-08', current: '2010-06-05'}))
.to.be(true);
});
});
});
describe('Boolean operators', function () {
it('should work', function () {
expect(py.eval("foo == 'foo' or foo == 'bar'",
{foo: 'bar'}))
.to.be(true);;
expect(py.eval("foo == 'foo' and bar == 'bar'",
{foo: 'foo', bar: 'bar'}))
.to.be(true);;
});
it('should be lazy', function () {
// second clause should nameerror if evaluated
expect(py.eval("foo == 'foo' or bar == 'bar'",
{foo: 'foo'}))
.to.be(true);;
expect(py.eval("foo == 'foo' and bar == 'bar'",
{foo: 'bar'}))
.to.be(false);;
});
it('should return the actual object', function () {
expect(py.eval('"foo" or "bar"')).to.be('foo');
expect(py.eval('None or "bar"')).to.be('bar');
expect(py.eval('False or None')).to.be(null);
expect(py.eval('0 or 1')).to.be(1);
});
});
describe('Containment', function () {
describe('in sequences', function () {
it('should match collection items', function () {
expect(py.eval("'bar' in ('foo', 'bar')"))
.to.be(true);
expect(py.eval('1 in (1, 2, 3, 4)'))
.to.be(true);;
expect(py.eval('1 in (2, 3, 4)'))
.to.be(false);;
expect(py.eval('"url" in ("url",)'))
.to.be(true);
expect(py.eval('"foo" in ["foo", "bar"]'))
.to.be(true);
});
it('should not be recursive', function () {
expect(py.eval('"ur" in ("url",)'))
.to.be(false);;
});
it('should be negatable', function () {
expect(py.eval('1 not in (2, 3, 4)')).to.be(true);
expect(py.eval('"ur" not in ("url",)')).to.be(true);
expect(py.eval('-2 not in (1, 2, 3)')).to.be(true);
});
});
describe('in dict', function () {
// TODO
});
describe('in strings', function () {
it('should match the whole string', function () {
expect(py.eval('"view" in "view"')).to.be(true);
expect(py.eval('"bob" in "view"')).to.be(false);
});
it('should match substrings', function () {
expect(py.eval('"ur" in "url"')).to.be(true);
});
});
});
describe('Conversions', function () {
describe('to bool', function () {
describe('strings', function () {
it('should be true if non-empty', function () {
expect(py.eval('bool(date_deadline)',
{date_deadline: '2008'}))
.to.be(true);
});
it('should be false if empty', function () {
expect(py.eval('bool(s)', {s: ''})) .to.be(false);
});
});
});
});
describe('Attribute access', function () {
it("should return the attribute's value", function () {
var o = new py.object();
o.bar = py.True;
expect(py.eval('foo.bar', {foo: o})).to.be(true);
o.bar = py.False;
expect(py.eval('foo.bar', {foo: o})).to.be(false);
});
it("should work with functions", function () {
var o = new py.object();
o.bar = new py.def(function () {
return new py.str("ok");
});
expect(py.eval('foo.bar()', {foo: o})).to.be('ok');
});
it('should work on instance attributes', function () {
var typ = py.type(function MyType() {
this.attr = new py.float(3);
}, py.object, {});
expect(py.eval('MyType().attr', {MyType: typ})).to.be(3);
});
it('should work on class attributes', function () {
var typ = py.type(function MyType() {}, py.object, {
attr: new py.float(3)
});
expect(py.eval('MyType().attr', {MyType: typ})).to.be(3);
});
it('should work with methods', function () {
var typ = py.type(function MyType() {
this.attr = new py.float(3);
}, py.object, {
some_method: function () { return new py.str('ok'); },
get_attr: function () { return this.attr; }
});
expect(py.eval('MyType().some_method()', {MyType: typ})).to.be('ok');
expect(py.eval('MyType().get_attr()', {MyType: typ})).to.be(3);
});
});
describe('Callables', function () {
it('should wrap JS functions', function () {
expect(py.eval('foo()', {foo: function foo() { return new py.float(3); }}))
.to.be(3);
});
it('should work on custom types', function () {
var typ = py.type(function MyType() {}, py.object, {
toJSON: function () { return true; }
});
expect(py.eval('MyType()', {MyType: typ})).to.be(true);
});
});
describe('issubclass', function () {
it('should say a type is its own subclass', function () {
expect(py.issubclass.__call__(py.dict, py.dict).toJSON())
.to.be(true);
expect(py.eval('issubclass(dict, dict)'))
.to.be(true);
});
it('should work with subtypes', function () {
expect(py.issubclass.__call__(py.bool, py.object).toJSON())
.to.be(true);
});
});
describe('builtins', function () {
it('should aways be available', function () {
expect(py.eval('bool("foo")')).to.be(true);
});
});

View File

@ -1,5 +0,0 @@
repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea
node: 87fb1b67d6a13f10a1a328104ee4d4b2c36801ec
branch: default
latesttag: 0.2
latesttagdistance: 1

View File

@ -1 +0,0 @@
Parser and evaluator of Python expressions

View File

@ -1,14 +0,0 @@
* Parser
since parsing expressions, try with a pratt parser
http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
http://effbot.org/zone/simple-top-down-parsing.htm
Evaluator
---------
* Stop busyworking trivial binary operator
* Make it *trivial* to build Python type-wrappers
* Implement Python's `data model
protocols<http://docs.python.org/reference/datamodel.html#basic-customization>`_
for *all* supported operations, optimizations can come later
* Automatically type-wrap everything (for now anyway)

View File

@ -1,546 +0,0 @@
var py = {};
(function (exports) {
var NUMBER = /^\d$/,
NAME_FIRST = /^[a-zA-Z_]$/,
NAME = /^[a-zA-Z0-9_]$/;
var create = function (o, props) {
function F() {}
F.prototype = o;
var inst = new F;
for(var name in props) {
if(!props.hasOwnProperty(name)) { continue; }
inst[name] = props[name];
}
return inst;
};
var symbols = {};
var comparators = {};
var Base = {
nud: function () { throw new Error(this.id + " undefined as prefix"); },
led: function (led) { throw new Error(this.id + " undefined as infix"); },
toString: function () {
if (this.id === '(constant)' || this.id === '(number)' || this.id === '(name)' || this.id === '(string)') {
return [this.id.slice(0, this.id.length-1), ' ', this.value, ')'].join('');
} else if (this.id === '(end)') {
return '(end)';
} else if (this.id === '(comparator)' ) {
var repr = ['(comparator', this.expressions[0]];
for (var i=0;i<this.operators.length; ++i) {
repr.push(this.operators[i], this.expressions[i+1]);
}
return repr.join(' ') + ')';
}
var out = [this.id, this.first, this.second, this.third]
.filter(function (r){return r}).join(' ');
return '(' + out + ')';
}
};
function symbol(id, bp) {
bp = bp || 0;
var s = symbols[id];
if (s) {
if (bp > s.lbp) {
s.lbp = bp;
}
return s;
}
return symbols[id] = create(Base, {
id: id,
lbp: bp
});
}
function constant(id) {
symbol(id).nud = function () {
this.id = "(constant)";
this.value = id;
return this;
};
}
function prefix(id, bp, nud) {
symbol(id).nud = nud || function () {
this.first = expression(bp);
return this
}
}
function infix(id, bp, led) {
symbol(id, bp).led = led || function (left) {
this.first = left;
this.second = expression(bp);
return this;
}
}
function infixr(id, bp) {
symbol(id, bp).led = function (left) {
this.first = left;
this.second = expression(bp - 1);
return this;
}
}
function comparator(id) {
comparators[id] = true;
var bp = 60;
infix(id, bp, function (left) {
this.id = '(comparator)';
this.operators = [id];
this.expressions = [left, expression(bp)];
while (token.id in comparators) {
this.operators.push(token.id);
advance();
this.expressions.push(
expression(bp));
}
return this;
});
}
constant('None'); constant('False'); constant('True');
symbol('(number)').nud = function () { return this; };
symbol('(name)').nud = function () { return this; };
symbol('(string)').nud = function () { return this; };
symbol('(end)');
symbol(':'); symbol(')'); symbol(']'); symbol('}'); symbol(',');
symbol('else');
symbol('lambda', 20).nud = function () {
this.first = [];
if (token.id !== ':') {
for(;;) {
if (token.id !== '(name)') {
throw new Error('Excepted an argument name');
}
this.first.push(token);
advance();
if (token.id !== ',') {
break;
}
advance(',');
}
}
advance(':');
this.second = expression();
return this;
};
infix('if', 20, function (left) {
this.first = left;
this.second = expression();
advance('else');
this.third = expression();
return this;
});
infixr('or', 30); infixr('and', 40); prefix('not', 50);
comparator('in'); comparator('not in');
comparator('is'); comparator('is not');
comparator('<'); comparator('<=');
comparator('>'); comparator('>=');
comparator('<>'); comparator('!='); comparator('==');
infix('|', 70); infix('^', 80), infix('&', 90);
infix('<<', 100); infix('>>', 100);
infix('+', 110); infix('-', 110);
infix('*', 120); infix('/', 120);
infix('//', 120), infix('%', 120);
prefix('-', 130); prefix('+', 130); prefix('~', 130);
infixr('**', 140);
infix('.', 150, function (left) {
if (token.id !== '(name)') {
throw new Error('Expected attribute name, got ', token.id);
}
this.first = left;
this.second = token;
advance();
return this;
});
symbol('(', 150).nud = function () {
this.first = [];
var comma = false;
if (token.id !== ')') {
while (true) {
if (token.id === ')') {
break;
}
this.first.push(expression());
if (token.id !== ',') {
break;
}
comma = true;
advance(',');
}
}
advance(')');
if (!this.first.length || comma) {
return this;
} else {
return this.first[0];
}
};
symbol('(').led = function (left) {
this.first = left;
this.second = [];
if (token.id !== ")") {
for(;;) {
this.second.push(expression());
if (token.id !== ',') {
break;
}
advance(',');
}
}
advance(")");
return this;
};
infix('[', 150, function (left) {
this.first = left;
this.second = expression();
advance("]");
return this;
});
symbol('[').nud = function () {
this.first = [];
if (token.id !== ']') {
for (;;) {
if (token.id === ']') {
break;
}
this.first.push(expression());
if (token.id !== ',') {
break;
}
advance(',');
}
}
advance(']');
return this;
};
symbol('{').nud = function () {
this.first = [];
if (token.id !== '}') {
for(;;) {
if (token.id === '}') {
break;
}
var key = expression();
advance(':');
var value = expression();
this.first.push([key, value]);
if (token.id !== ',') {
break;
}
advance(',');
}
}
advance('}');
return this;
};
var longops = {
'*': ['*'],
'<': ['<', '=', '>'],
'>': ['=', '>'],
'!': ['='],
'=': ['='],
'/': ['/']
};
function Tokenizer() {
this.states = ['initial'];
this.tokens = [];
}
Tokenizer.prototype = {
builder: function (empty) {
var key = this.states[0] + '_builder';
if (empty) {
var value = this[key];
delete this[key];
return value;
} else {
return this[key] = this[key] || [];
}
},
simple: function (type) {
this.tokens.push({type: type});
},
push: function (new_state) {
this.states.push(new_state);
},
pop: function () {
this.states.pop();
},
feed: function (str, index) {
var s = this.states;
return this[s[s.length - 1]](str, index);
},
initial: function (str, index) {
var character = str[index];
if (character in longops) {
var follow = longops[character];
for(var i=0, len=follow.length; i<len; ++i) {
if (str[index+1] === follow[i]) {
character += follow[i];
index++;
break;
}
}
}
if (character === ' ') {
return index+1;
} else if (character === '\0') {
this.tokens.push(symbols['(end)']);
return index + 1
} else if (character === '"' || character === "'") {
this.push('string');
return index + 1;
} else if (NUMBER.test(character)) {
this.push('number');
return index;
} else if (NAME_FIRST.test(character)) {
this.push('name');
return index;
} else if (character in symbols) {
this.tokens.push(create(symbols[character]));
return index + 1;
}
throw new Error("Tokenizing failure of <<" + str + ">> at index " + index
+ ", character [[" + character + "]]"
+ "; parsed so far: " + this.tokens);
},
string: function (str, index) {
var character = str[index];
if (character === '"' || character === "'") {
this.tokens.push(create(symbols['(string)'], {
value: this.builder(true).join('')
}));
this.pop();
return index + 1;
}
this.builder().push(character);
return index + 1;
},
number: function (str, index) {
var character = str[index];
if (!(character == '.' || NUMBER.test(character))) {
this.tokens.push(create(symbols['(number)'], {
value: parseFloat(this.builder(true).join(''))
}));
this.pop();
return index;
}
this.builder().push(character);
return index + 1;
},
name: function (str, index) {
var character = str[index];
if (!NAME.test(character)) {
var name = this.builder(true).join('');
var symbol = symbols[name];
if (symbol) {
if (name === 'in' && this.tokens[this.tokens.length-1].id === 'not') {
symbol = symbols['not in'];
this.tokens.pop();
} else if (name === 'not' && this.tokens[this.tokens.length-1].id === 'is') {
symbol = symbols['is not'];
this.tokens.pop();
}
this.tokens.push(create(symbol));
} else {
this.tokens.push(create(symbols['(name)'], {
value: name
}));
}
this.pop();
return index;
}
this.builder().push(character);
return index + 1;
}
};
exports.tokenize = function tokenize(str) {
var index = 0,
tokenizer = new Tokenizer(str);
str += '\0';
do {
index = tokenizer.feed(str, index);
} while (index !== str.length);
return tokenizer.tokens;
};
var token, next;
function expression(rbp) {
rbp = rbp || 0;
var t = token;
token = next();
var left = t.nud();
while (rbp < token.lbp) {
t = token;
token = next();
left = t.led(left);
}
return left;
}
function advance(id) {
if (id && token.id !== id) {
throw new Error(
'Expected "' + id + '", got "' + token.id + '"');
}
token = next();
}
exports.object = create({}, {});
exports.bool = function (arg) { return !!arg; };
exports.tuple = create(exports.object, {
__contains__: function (value) {
for(var i=0, len=this.values.length; i<len; ++i) {
if (this.values[i] === value) {
return true;
}
}
return false;
},
toJSON: function () {
return this.values;
}
});
exports.list = exports.tuple;
exports.dict = create(exports.object, {
toJSON: function () {
return this.values;
}
});
exports.parse = function (toks) {
var index = 0;
token = toks[0];
next = function () { return toks[++index]; };
return expression();
};
var evaluate_operator = function (operator, a, b) {
switch (operator) {
case '==': case 'is': return a === b;
case '!=': case 'is not': return a !== b;
case '<': return a < b;
case '<=': return a <= b;
case '>': return a > b;
case '>=': return a >= b;
case 'in':
if (typeof b === 'string') {
return b.indexOf(a) !== -1;
}
return b.__contains__(a);
case 'not in':
if (typeof b === 'string') {
return b.indexOf(a) === -1;
}
return !b.__contains__(a);
}
throw new Error('SyntaxError: unknown comparator [[' + operator + ']]');
};
exports.evaluate = function (expr, context) {
switch (expr.id) {
case '(name)':
var val = context[expr.value];
if (val === undefined) {
throw new Error("NameError: name '" + expr.value + "' is not defined");
}
return val;
case '(string)':
case '(number)':
return expr.value;
case '(constant)':
if (expr.value === 'None')
return null;
else if (expr.value === 'False')
return false;
else if (expr.value === 'True')
return true;
throw new Error("SyntaxError: unknown constant '" + expr.value + "'");
case '(comparator)':
var result, left = exports.evaluate(expr.expressions[0], context);
for(var i=0; i<expr.operators.length; ++i) {
result = evaluate_operator(
expr.operators[i],
left,
left = exports.evaluate(expr.expressions[i+1], context));
if (!result) { return false; }
}
return true;
case '-':
if (expr.second) {
throw new Error('SyntaxError: binary [-] not implemented yet');
}
return -(exports.evaluate(expr.first, context));
case 'not':
return !(exports.evaluate(expr.first, context));
case 'and':
return (exports.evaluate(expr.first, context)
&& exports.evaluate(expr.second, context));
case 'or':
return (exports.evaluate(expr.first, context)
|| exports.evaluate(expr.second, context));
case '(':
if (expr.second) {
var fn = exports.evaluate(expr.first, context), args=[];
for (var jj=0; jj<expr.second.length; ++jj) {
args.push(exports.evaluate(
expr.second[jj], context));
}
return fn.apply(null, args);
}
var tuple_exprs = expr.first,
tuple_values = [];
for (var j=0, len=tuple_exprs.length; j<len; ++j) {
tuple_values.push(exports.evaluate(
tuple_exprs[j], context));
}
return create(exports.tuple, {values: tuple_values});
case '[':
if (expr.second) {
throw new Error('SyntaxError: indexing not implemented yet');
}
var list_exprs = expr.first, list_values = [];
for (var k=0; k<list_exprs.length; ++k) {
list_values.push(exports.evaluate(
list_exprs[k], context));
}
return create(exports.list, {values: list_values});
case '{':
var dict_exprs = expr.first, dict_values = {};
for(var l=0; l<dict_exprs.length; ++l) {
dict_values[exports.evaluate(dict_exprs[l][0], context)] =
exports.evaluate(dict_exprs[l][1], context);
}
return create(exports.dict, {values: dict_values});
case '.':
if (expr.second.id !== '(name)') {
throw new Error('SyntaxError: ' + expr);
}
return exports.evaluate(expr.first, context)[expr.second.value];
default:
throw new Error('SyntaxError: Unknown node [[' + expr.id + ']]');
}
};
exports.eval = function (str, context) {
return exports.evaluate(
exports.parse(
exports.tokenize(
str)),
context);
}
})(typeof exports === 'undefined' ? py : exports);

View File

@ -1,90 +0,0 @@
var py = require('../lib/py.js'),
assert = require('assert');
// Literals
assert.strictEqual(py.eval('1'), 1);
assert.strictEqual(py.eval('None'), null);
assert.strictEqual(py.eval('False'), false);
assert.strictEqual(py.eval('True'), true);
assert.strictEqual(py.eval('"somestring"'), 'somestring');
assert.strictEqual(py.eval("'somestring'"), 'somestring');
assert.deepEqual(py.eval("()").toJSON(), []);
assert.deepEqual(py.eval("[]").toJSON(), []);
assert.deepEqual(py.eval("{}").toJSON(), {});
assert.deepEqual(py.eval("(None, True, False, 0, 1, 'foo')").toJSON(),
[null, true, false, 0, 1, 'foo']);
assert.deepEqual(py.eval("[None, True, False, 0, 1, 'foo']").toJSON(),
[null, true, false, 0, 1, 'foo']);
assert.deepEqual(py.eval("{'foo': 1, foo: 2}", {foo: 'bar'}).toJSON(),
{foo: 1, bar: 2});
// Equality tests
assert.ok(py.eval(
"foo == 'foo'", {foo: 'foo'}));
// Inequality
assert.ok(py.eval(
"foo != bar", {foo: 'foo', bar: 'bar'}));
// Comparisons
assert.ok(py.eval('3 < 5'));
assert.ok(py.eval('5 >= 3'));
assert.ok(py.eval('3 >= 3'));
assert.ok(!py.eval('5 < 3'));
assert.ok(py.eval('1 < 3 < 5'));
assert.ok(py.eval('5 > 3 > 1'));
assert.ok(py.eval('1 < 3 > 2 == 2 > -2 not in (0, 1, 2)'));
// string rich comparisons
assert.ok(py.eval(
'date >= current', {date: '2010-06-08', current: '2010-06-05'}));
// Boolean operators
assert.ok(py.eval(
"foo == 'foo' or foo == 'bar'", {foo: 'bar'}));
assert.ok(py.eval(
"foo == 'foo' and bar == 'bar'", {foo: 'foo', bar: 'bar'}));
// - lazyness, second clauses NameError if not short-circuited
assert.ok(py.eval(
"foo == 'foo' or bar == 'bar'", {foo: 'foo'}));
assert.ok(!py.eval(
"foo == 'foo' and bar == 'bar'", {foo: 'bar'}));
// contains (in)
assert.ok(py.eval(
"foo in ('foo', 'bar')", {foo: 'bar'}));
assert.ok(py.eval('1 in (1, 2, 3, 4)'));
assert.ok(!py.eval('1 in (2, 3, 4)'));
assert.ok(py.eval('type in ("url",)', {type: 'url'}));
assert.ok(!py.eval('type in ("url",)', {type: 'ur'}));
assert.ok(py.eval('1 not in (2, 3, 4)'));
assert.ok(py.eval('type not in ("url",)', {type: 'ur'}));
assert.ok(py.eval(
"foo in ['foo', 'bar']", {foo: 'bar'}));
// string contains
assert.ok(py.eval('type in "view"', {type: 'view'}));
assert.ok(!py.eval('type in "view"', {type: 'bob'}));
assert.ok(py.eval('type in "url"', {type: 'ur'}));
// Literals
assert.strictEqual(py.eval('False'), false);
assert.strictEqual(py.eval('True'), true);
assert.strictEqual(py.eval('None'), null);
assert.ok(py.eval('foo == False', {foo: false}));
assert.ok(!py.eval('foo == False', {foo: true}));
// conversions
assert.strictEqual(
py.eval('bool(date_deadline)', {bool: py.bool, date_deadline: '2008'}),
true);
// getattr
assert.ok(py.eval('foo.bar', {foo: {bar: true}}));
assert.ok(!py.eval('foo.bar', {foo: {bar: false}}));
// complex expressions
assert.ok(py.eval(
"state=='pending' and not(date_deadline and (date_deadline < current_date))",
{state: 'pending', date_deadline: false}));
assert.ok(py.eval(
"state=='pending' and not(date_deadline and (date_deadline < current_date))",
{state: 'pending', date_deadline: '2010-05-08', current_date: '2010-05-08'}));;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,201 @@
// Mixins {{{
@mixin vertical-gradient($startColor: #555, $endColor: #333)
background: $startColor
background: -moz-linear-gradient($startColor, $endColor)
background: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor))
background: -webkit-linear-gradient($startColor, $endColor)
@mixin radial-gradient($gradient)
background-position: center center
background-image: radial-gradient($gradient)
background-image: -moz-radial-gradient($gradient)
background-image: -webkit-radial-gradient(circle, $gradient)
@mixin radius($radius: 5px)
-moz-border-radius: $radius
-webkit-border-radius: $radius
border-radius: $radius
@mixin box-shadow($bsval: 0px 1px 4px #777)
-moz-box-shadow: $bsval
-webkit-box-shadow: $bsval
-box-shadow: $bsval
@mixin transition($transval: (border linear 0.2s, box-shadow linear 0.2s))
-webkit-transition: $transval
-moz-transition: $transval
-ms-transition: $transval
-o-transition: $transval
transition: $transval
@mixin opacity($opacity: .5)
filter: alpha(opacity=$opacity * 100)
-khtml-opacity: $opacity
-moz-opacity: $opacity
opacity: $opacity
@mixin background-clip($clip: padding-box)
-webkit-background-clip: $clip
-moz-background-clip: $clip
background-clip: $clip
// }}}
.openerp2
// Common styles {{{
padding: 0
margin: 0
font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif
color: #4c4c4c
font-size: 13px
background: white
position: relative
a
text-decoration: none
// }}}
// WebClient {{{
.oe_webclient
position: absolute
top: 0
bottom: 0
left: 0
right: 0
// }}}
// Topbar {{{
.oe_topbar
width: 100%
height: 31px
border-top: solid 1px #d3d3d3
border-bottom: solid 1px black
@include vertical-gradient(#646060, #262626)
.oe_topbar_item
li
float: left
a
display: block
padding: 5px 10px 7px
line-height: 20px
height: 20px
color: #eee
vertical-align: top
text-shadow: 0 1px 1px rgba(0,0,0,0.2)
&:hover
background: #303030
color: white
@include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
.oe_active
background: #303030
font-weight: bold
color: white
@include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
.oe_topbar_avatar
width: 24px
height: 24px
margin: -2px 2px 0 0
@include radius(4px)
// }}}
// Menu {{{
.oe_menu
float: left
padding: 0
margin: 0
li
list-style-type: none
float: left
a
display: block
padding: 5px 10px 7px
line-height: 20px
height: 20px
color: #eee
vertical-align: top
text-shadow: 0 1px 1px rgba(0,0,0,0.2)
&:hover
background: #303030
color: white
@include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
.oe_active
background: #303030
font-weight: bold
color: white
@include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
// }}}
// DropDown Menu {{{
.oe_dropdown_menu
float: right
padding: 0
margin: 0
li
list-style-type: none
float: left
.oe_dropdown
position: relative
.dropdown-toggle:after
width: 0
height: 0
display: inline-block
content: "&darr"
text-indent: -99999px
vertical-align: top
margin-top: 8px
margin-left: 4px
border-left: 4px solid transparent
border-right: 4px solid transparent
border-top: 4px solid white
@include opacity(0.5)
.oe_dropdown_options
float: left
background: #333
background: rgba(37,37,37,0.9)
display: none
position: absolute
top: 32px
right: -1px
border: 0
z-index: 900
width: 160px
margin-left: 0
margin-right: 0
padding: 6px 0
zoom: 1
border-color: #999
border-color: rgba(0, 0, 0, 0.2)
border-style: solid
border-width: 0 1px 1px
@include radius(0 0 6px 6px)
@include box-shadow(0 1px 4px rgba(0,0,0,0.3))
@include background-clip()
li
float: none
display: block
background-color: none
a
display: block
padding: 4px 15px
clear: both
font-weight: normal
line-height: 18px
color: #eee
&:hover
@include vertical-gradient(#292929, #191919)
@include box-shadow(none)
// }}}
.openerp
// Transitional overrides for old styles {{{
.oe-shortcuts
position: static
#oe_header
clear: both
// }}}
// au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers <afile> > "%:p:r.css"
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:

File diff suppressed because it is too large Load Diff

View File

@ -846,29 +846,14 @@ openerp.web.Header = openerp.web.OldWidget.extend(/** @lends openerp.web.Header
}
});
openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */{
openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{
/**
* @constructs openerp.web.Menu
* @extends openerp.web.OldWidget
* @extends openerp.web.Widget
*
* @param parent
* @param element_id
* @param secondary_menu_id
*/
init: function(parent, element_id, secondary_menu_id) {
this._super(parent, element_id);
this.secondary_menu_id = secondary_menu_id;
this.$secondary_menu = $("#" + secondary_menu_id);
this.menu = false;
this.folded = false;
if (window.localStorage) {
this.folded = localStorage.getItem('oe_menu_folded') === 'true';
}
this.float_timeout = 700;
},
start: function() {
this.$secondary_menu.addClass(this.folded ? 'oe_folded' : 'oe_unfolded');
},
template: 'Menu',
do_reload: function() {
var self = this;
return this.rpc("/web/menu/load", {}, this.on_loaded).then(function () {
@ -879,22 +864,13 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
},
on_loaded: function(data) {
this.data = data;
this.$element.html(QWeb.render("Menu", { widget : this }));
this.render_element();
if (!this.$secondary_menu && this.getParent()) {
// TODO: create Widget openerp.web.SubMenu
this.$secondary_menu = this.getParent().$element.find('.oe_secondary_menu');
}
this.$secondary_menu.html(QWeb.render("Menu.secondary", { widget : this }));
this.$element.add(this.$secondary_menu).find("a").click(this.on_menu_click);
this.$secondary_menu.find('.oe_toggle_secondary_menu').click(this.on_toggle_fold);
},
on_toggle_fold: function() {
this.$secondary_menu.toggleClass('oe_folded').toggleClass('oe_unfolded');
if (this.folded) {
this.$secondary_menu.find('.oe_secondary_menu.active').show();
} else {
this.$secondary_menu.find('.oe_secondary_menu').hide();
}
this.folded = !this.folded;
if (window.localStorage) {
localStorage.setItem('oe_menu_folded', this.folded.toString());
}
},
/**
* Opens a given menu by id, as if a user had browsed to that menu by hand
@ -903,8 +879,8 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
* @param {Number} menu_id database id of the terminal menu to select
*/
open_menu: function (menu_id) {
this.$element.add(this.$secondary_menu).find('.active')
.removeClass('active');
this.$element.add(this.$secondary_menu).find('.oe_active')
.removeClass('oe_active');
this.$secondary_menu.find('> .oe_secondary_menu').hide();
var $primary_menu;
@ -913,7 +889,7 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
if ($secondary_submenu.length) {
for(;;) {
if ($secondary_submenu.hasClass('leaf')) {
$secondary_submenu.addClass('active');
$secondary_submenu.addClass('oe_active');
} else if ($secondary_submenu.hasClass('submenu')) {
$secondary_submenu.addClass('opened')
}
@ -932,9 +908,9 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
if (!$primary_menu.length) {
return;
}
$primary_menu.addClass('active');
$primary_menu.addClass('oe_active');
this.$secondary_menu.find(
'div[data-menu-parent=' + $primary_menu.data('menu') + ']').addClass('active').toggle(!this.folded);
'div[data-menu-parent=' + $primary_menu.data('menu') + ']').addClass('oe_active');
},
on_menu_click: function(ev, id) {
id = id || 0;
@ -964,7 +940,7 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
},
do_menu_click: function($clicked_menu, manual) {
var $sub_menu, $main_menu,
active = $clicked_menu.is('.active'),
active = $clicked_menu.is('.oe_active'),
sub_menu_visible = false,
has_submenu_items = false;
@ -980,11 +956,11 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
has_submenu_items = !!$sub_menu.children().length;
this.$secondary_menu.find('.oe_secondary_menu').hide();
$('.active', this.$element.add(this.$secondary_menu)).removeClass('active');
$main_menu.add($clicked_menu).add($sub_menu).addClass('active');
$('.oe_active', this.$element.add(this.$secondary_menu)).removeClass('oe_active');
$main_menu.add($clicked_menu).add($sub_menu).addClass('oe_active');
if (has_submenu_items) {
if (!(this.folded && manual)) {
if (!manual) {
this.do_show_secondary($sub_menu, $main_menu);
} else {
this.do_show_secondary();
@ -1000,22 +976,15 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
});
$clicked_menu.toggleClass('opened').next().toggle();
} else if ($clicked_menu.is('.leaf')) {
$sub_menu.toggle(!this.folded);
return true;
}
} else if (this.folded) {
if ((active && sub_menu_visible) || !has_submenu_items) {
$sub_menu.hide();
return true;
}
return manual;
} else {
return true;
}
return false;
},
do_hide_secondary: function() {
this.$secondary_menu.hide();
//this.$secondary_menu.hide();
},
do_show_secondary: function($sub_menu, $main_menu) {
var self = this;
@ -1023,32 +992,6 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
if (!arguments.length) {
return;
}
if (this.folded) {
var css = $main_menu.position(),
fold_width = this.$secondary_menu.width() + 2,
window_width = $(window).width();
css.top += 33;
css.left -= Math.round(($sub_menu.width() - $main_menu.width()) / 2);
css.left = css.left < fold_width ? fold_width : css.left;
if ((css.left + $sub_menu.width()) > window_width) {
delete(css.left);
css.right = 1;
}
$sub_menu.css(css);
$sub_menu.mouseenter(function() {
clearTimeout($sub_menu.data('timeoutId'));
$sub_menu.data('timeoutId', null);
return false;
}).mouseleave(function(evt) {
var timeoutId = setTimeout(function() {
if (self.folded && $sub_menu.data('timeoutId')) {
$sub_menu.hide().unbind('mouseenter').unbind('mouseleave');
}
}, self.float_timeout);
$sub_menu.data('timeoutId', timeoutId);
return false;
});
}
$sub_menu.show();
},
on_menu_action_loaded: function(data) {
@ -1065,12 +1008,82 @@ openerp.web.Menu = openerp.web.OldWidget.extend(/** @lends openerp.web.Menu# */
}
});
openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebClient */{
openerp.web.DropDownMenu = openerp.web.Widget.extend(/** @lends openerp.web.Header# */{
template: "DropDownMenu",
/**
* @constructs openerp.web.WebClient
* @constructs openerp.web.DropDownMenu
* @extends openerp.web.OldWidget
*
* @param element_id
* @param parent
*/
init: function(parent) {
this._super(parent);
},
start: function() {
var self = this;
this._super.apply(this, arguments);
$('html').bind('click', function() {
self.$element.find('.oe_dropdown_options').hide();
});
this.$element.find('.oe_dropdown_toggle').click(function() {
self.$element.find('.oe_dropdown_options').toggle();
return false;
});
this.$element.find('.oe_dropdown_options li a').click(function() {
var f = self['on_menu_' + $(this).data('menu')];
f && f();
self.$element.find('.oe_dropdown_options').hide();
return false;
});
},
on_menu_logout: function() {
},
on_menu_settings: function() {
var self = this;
var action_manager = new openerp.web.ActionManager(this);
var dataset = new openerp.web.DataSet (this,'res.users',this.context);
dataset.call ('action_get','',function (result){
self.rpc('/web/action/load', {action_id:result}, function(result){
action_manager.do_action(_.extend(result['result'], {
res_id: self.session.uid,
res_model: 'res.users',
flags: {
action_buttons: false,
search_view: false,
sidebar: false,
views_switcher: false,
pager: false
}
}));
});
});
this.dialog = new openerp.web.Dialog(this,{
title: _t("Preferences"),
width: '700px',
buttons: [
{text: _t("Cancel"), click: function(){ $(this).dialog('destroy'); }},
{text: _t("Change password"), click: function(){ self.change_password(); }},
{text: _t("Save"), click: function(){
var inner_viewmanager = action_manager.inner_viewmanager;
inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save()
.then(function() {
self.dialog.destroy();
// needs to refresh interface in case language changed
window.location.reload();
});
}
}
]
}).open();
action_manager.appendTo(this.dialog);
action_manager.render(this.dialog);
}
});
openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClient */{
/**
* @constructs openerp.web.WebClient
* @extends openerp.web.Widget
*/
init: function(parent) {
var self = this;
@ -1079,11 +1092,9 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
this._current_state = null;
},
render_element: function() {
this.$element.addClass("openerp");
},
start: function() {
var self = this;
this.$element.addClass("openerp openerp2");
if (jQuery.param != undefined && jQuery.deparam(jQuery.param.querystring()).kitten != undefined) {
this.$element.addClass("kitten-mode-activated");
this.$element.delegate('img.oe-record-edit-link-img', 'hover', function(e) {
@ -1123,15 +1134,18 @@ openerp.web.WebClient = openerp.web.OldWidget.extend(/** @lends openerp.web.WebC
var self = this;
this.destroy_content();
this.show_common();
self.$table = $(QWeb.render("Interface", {}));
self.$table = $(QWeb.render("WebClient", {}));
self.$element.append(self.$table);
self.header = new openerp.web.Header(self);
self.header.on_logout.add(this.proxy('on_logout'));
self.header.on_action.add(this.proxy('on_menu_action'));
self.header.appendTo($("#oe_header"));
self.menu = new openerp.web.Menu(self, "oe_menu", "oe_secondary_menu");
self.menu = new openerp.web.Menu(self);
self.menu.replace(this.$element.find('.oe_menu_placeholder'));
self.menu.on_action.add(this.proxy('on_menu_action'));
self.menu.start();
self.dropdown_menu = new openerp.web.DropDownMenu(self);
self.dropdown_menu.replace(this.$element.find('.oe_dropdown_menu_placeholder'));
self.dropdown_menu.on_menu_logout.add(this.proxy('on_logout'));
},
show_common: function() {
var self = this;

View File

@ -983,7 +983,7 @@ openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.we
if (translation_bundle.lang_parameters) {
this.parameters = translation_bundle.lang_parameters;
this.parameters.grouping = py.eval(
this.parameters.grouping).toJSON();
this.parameters.grouping);
}
},
add_module_translation: function(mod) {

View File

@ -158,7 +158,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView#
var pair = this.colors[i],
color = pair[0],
expression = pair[1];
if (py.evaluate(expression, _.extend({bool: py.bool}, context))) {
if (py.evaluate(expression, context).toJSON()) {
return 'color: ' + color + ';';
}
// TODO: handle evaluation errors

View File

@ -146,7 +146,7 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView#
var pair = this.colors[i],
color = pair[0],
expression = pair[1];
if (py.evaluate(expression, _.extend({bool: py.bool}, context))) {
if (py.evaluate(expression, context).toJSON()) {
return 'color: ' + color + ';';
}
// TODO: handle evaluation errors

View File

@ -17,35 +17,39 @@
</div>
</div>
</t>
<t t-name="Interface">
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="100%" class="main_table">
<tr>
<td colspan="2" valign="top">
<div id="oe_header" class="header"></div>
<div id="oe_menu" class="menu"></div>
</td>
</tr>
<tr>
<td colspan="2" valign="top" height="100%">
<t t-name="WebClient">
<div class="oe_webclient">
<div class="oe_topbar">
<div class="oe_menu_placeholder"/>
<div class="oe_dropdown_menu_placeholder"/>
</div>
<div id="oe_header" class="header" style="display: none"></div>
<div class="oe_leftbar">
<!--
<a href="#" class="oe_logo"><img src="img/logo.png"/></a>
<div class="oe_secondary_menu"/>
<div class="oe_footer">
Powered by <a href="http://www.openerp.com" class="openerp"><span class="red">Open</span>ERP</a>
</div>
-->
</div>
<div class="oe_application">
<!-- This table will be removed as soon as we decide what will happen to the sidebar -->
<table cellspacing="0" cellpadding="0" border="0" height="100%" width="100%">
<tr>
<td valign="top" id="oe_secondary_menu" class="secondary_menu"></td>
<td valign="top" class="secondary_menu">
<div class="oe_secondary_menu"/>
</td>
<td valign="top" class="oe-application-container">
<div id="oe_app" class="oe-application">
</div>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td colspan="2">
<div id="oe_footer" class="oe_footer">
<p class="oe_footer_powered">Powered by <a href="http://www.openerp.com">OpenERP</a></p>
</div>
</td>
</tr>
</table>
</div>
</div>
</t>
<t t-name="Loading">
<div id="oe_loading">
@ -372,22 +376,17 @@
t-att-data-shortcut-id="shortcut.id"
><t t-esc="shortcut.name"/></li>
</ul>
<t t-name="Menu">
<table align="center">
<tr>
<td t-foreach="widget.data.data.children" t-as="menu">
<ul class="oe_menu" t-if="widget.data">
<li t-foreach="widget.data.data.children" t-as="menu">
<a href="#" t-att-data-menu="menu.id">
<t t-esc="menu.name"/>
</a>
</td>
</tr>
</table>
</li>
</ul>
</t>
<t t-name="Menu.secondary">
<div t-attf-class="oe_toggle_secondary_menu">
<span class="oe_menu_fold" title="Fold menu"><t t-raw="'&amp;laquo;'"/></span>
<span class="oe_menu_unfold" title="Unfold menu"><t t-raw="'&amp;raquo;'"/></span>
</div>
<div t-foreach="widget.data.data.children" t-as="menu" style="display: none" class="oe_secondary_menu" t-att-data-menu-parent="menu.id">
<t t-foreach="menu.children" t-as="menu">
<t t-set="classname" t-translation="off">oe_secondary_menu_item</t>
@ -410,6 +409,23 @@
</t>
</div>
</t>
<t t-name="DropDownMenu">
<ul class="oe_dropdown_menu oe_topbar_item">
<li class="oe_dropdown">
<a href="#" class="oe_dropdown_toggle">
<img class="oe_topbar_avatar" src="http://www.amigrave.com/ClarkGableSmall.jpg"/>
Clark Gable
</a>
<ul class="oe_dropdown_options">
<!--<li><a href="#" data-menu="profile">Profile</a></li>-->
<li><a href="#" data-menu="settings">Settings</a></li>
<li><a href="#" data-menu="logout">Log out</a></li>
</ul>
</li>
</ul>
</t>
<t t-name="ViewManager">
<table class="view-manager-main-table" cellpadding="0" cellspacing="0">
<tbody>

View File

@ -8,14 +8,14 @@ msgstr ""
"Project-Id-Version: openerp-web\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-02-14 15:27+0100\n"
"PO-Revision-Date: 2012-02-09 20:10+0000\n"
"PO-Revision-Date: 2012-02-24 11:28+0000\n"
"Last-Translator: Ahmet Altınışık <Unknown>\n"
"Language-Team: Turkish <tr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2012-02-15 05:43+0000\n"
"X-Generator: Launchpad (build 14781)\n"
"X-Launchpad-Export-Date: 2012-02-25 05:30+0000\n"
"X-Generator: Launchpad (build 14860)\n"
#. openerp-web
#: addons/web_dashboard/static/src/js/dashboard.js:63
@ -36,12 +36,12 @@ msgstr "Sınıflandırılmamış"
#: addons/web_dashboard/static/src/js/dashboard.js:324
#, python-format
msgid "Execute task \"%s\""
msgstr ""
msgstr "Görevi Çalıştır \"%s\""
#. openerp-web
#: addons/web_dashboard/static/src/js/dashboard.js:325
msgid "Mark this task as done"
msgstr ""
msgstr "Bu görevi yapıldı olarak işaretle"
#. openerp-web
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4