[MERGE] upstream

bzr revid: fme@openerp.com-20130807100643-0olinq3584kzc3we
This commit is contained in:
Fabien Meghazi 2013-08-07 12:06:43 +02:00
commit 11fb809e60
40 changed files with 10906 additions and 10428 deletions

3
addons/web/.bowerrc Normal file
View File

@ -0,0 +1,3 @@
{
"directory": "static/lib/"
}

View File

@ -13,13 +13,12 @@ This module provides the core of the OpenERP Web Client.
'auto_install': True,
'post_load': 'wsgi_postload',
'js' : [
"static/src/fixbind.js",
"static/lib/datejs/globalization/en-US.js",
"static/lib/datejs/core.js",
"static/lib/datejs/parser.js",
"static/lib/datejs/sugarpak.js",
"static/lib/datejs/extras.js",
"static/lib/jquery/jquery-1.8.3.js",
"static/lib/jquery/jquery.js",
"static/lib/jquery.MD5/jquery.md5.js",
"static/lib/jquery.form/jquery.form.js",
"static/lib/jquery.validate/jquery.validate.js",
@ -39,7 +38,7 @@ This module provides the core of the OpenERP Web Client.
"static/lib/jquery.timeago/jquery.timeago.js",
"static/lib/qweb/qweb2.js",
"static/lib/underscore/underscore.js",
"static/lib/underscore/underscore.string.js",
"static/lib/underscore.string/lib/underscore.string.js",
"static/lib/backbone/backbone.js",
"static/lib/cleditor/jquery.cleditor.js",
"static/lib/py.js/lib/py.js",
@ -47,8 +46,7 @@ This module provides the core of the OpenERP Web Client.
"static/src/js/boot.js",
"static/src/js/testing.js",
"static/src/js/pyeval.js",
"static/src/js/corelib.js",
"static/src/js/coresetup.js",
"static/src/js/core.js",
"static/src/js/dates.js",
"static/src/js/formats.js",
"static/src/js/chrome.js",
@ -56,8 +54,8 @@ This module provides the core of the OpenERP Web Client.
"static/src/js/data.js",
"static/src/js/data_export.js",
"static/src/js/search.js",
"static/src/js/view_form.js",
"static/src/js/view_list.js",
"static/src/js/view_form.js",
"static/src/js/view_list_editable.js",
"static/src/js/view_tree.js",
],
@ -83,7 +81,8 @@ This module provides the core of the OpenERP Web Client.
"static/test/data.js",
"static/test/list-utils.js",
"static/test/formats.js",
"static/test/rpc.js",
"static/test/jsonrpc.js",
"static/test/rpc-misordered.js",
"static/test/evals.js",
"static/test/search.js",
"static/test/list.js",

17
addons/web/bower.json Normal file
View File

@ -0,0 +1,17 @@
{
"name": "web",
"version": "0.0.0",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"jquery": "1.8.3",
"underscore": "1.3.1",
"underscore.string": "2.2.1",
"qweb": "git@github.com:OpenERP/qweb.git#~1.0.0"
}
}

View File

@ -347,13 +347,7 @@ def make_conditional(response, last_modified=None, etag=None):
return response.make_conditional(request.httprequest)
def login_and_redirect(db, login, key, redirect_url='/'):
wsgienv = request.httprequest.environ
env = dict(
base_location=request.httprequest.url_root.rstrip('/'),
HTTP_HOST=wsgienv['HTTP_HOST'],
REMOTE_ADDR=wsgienv['REMOTE_ADDR'],
)
request.session.authenticate(db, login, key, env)
request.session.authenticate(db, login, key)
return set_cookie_and_redirect(redirect_url)
def set_cookie_and_redirect(redirect_url):
@ -605,7 +599,7 @@ class WebClient(http.Controller):
data = re.sub(
rx_url,
r"""url(\1%s/""" % (web_dir,),
r"url(\1%s/" % (web_dir,),
data,
)
return data.encode('utf-8')
@ -677,20 +671,26 @@ class WebClient(http.Controller):
return {"modules": translations_per_module,
"lang_parameters": None}
@http.route('/web/webclient/translations', type='json', auth="user")
def translations(self, mods, lang):
res_lang = request.session.model('res.lang')
ids = res_lang.search([("code", "=", lang)])
@http.route('/web/webclient/translations', type='json', auth="admin")
def translations(self, mods=None, lang=None):
if mods is None:
m = request.registry.get('ir.module.module')
mods = [x['name'] for x in m.search_read(request.cr, request.uid,
[('state','=','installed')], ['name'])]
if lang is None:
lang = request.context["lang"]
res_lang = request.registry.get('res.lang')
ids = res_lang.search(request.cr, request.uid, [("code", "=", lang)])
lang_params = None
if ids:
lang_params = res_lang.read(ids[0], ["direction", "date_format", "time_format",
lang_params = res_lang.read(request.cr, request.uid, ids[0], ["direction", "date_format", "time_format",
"grouping", "decimal_point", "thousands_sep"])
# Regional languages (ll_CC) must inherit/override their parent lang (ll), but this is
# done server-side when the language is loaded, so we only need to load the user's lang.
ir_translation = request.session.model('ir.translation')
ir_translation = request.registry.get('ir.translation')
translations_per_module = {}
messages = ir_translation.search_read([('module','in',mods),('lang','=',lang),
messages = ir_translation.search_read(request.cr, request.uid, [('module','in',mods),('lang','=',lang),
('comments','like','openerp-web'),('value','!=',False),
('value','!=','')],
['module','src','value','lang'], order='module')
@ -832,13 +832,7 @@ class Session(http.Controller):
@http.route('/web/session/authenticate', type='json', auth="none")
def authenticate(self, db, login, password, base_location=None):
wsgienv = request.httprequest.environ
env = dict(
base_location=base_location,
HTTP_HOST=wsgienv['HTTP_HOST'],
REMOTE_ADDR=wsgienv['REMOTE_ADDR'],
)
request.session.authenticate(db, login, password, env)
request.session.authenticate(db, login, password)
return self.session_info()
@ -1083,7 +1077,10 @@ class DataSet(http.Controller):
names.get(record['id']) or "%s#%d" % (model, (record['id']))
return records
return getattr(request.session.model(model), method)(*args, **kwargs)
if method.startswith('_'):
raise Exception("Access denied")
return getattr(request.registry.get(model), method)(request.cr, request.uid, *args, **kwargs)
@http.route('/web/dataset/call', type='json', auth="user")
def call(self, model, method, args, domain_id=None, context_id=None):

View File

@ -13,6 +13,8 @@ from openerp.modules import module
from .main import module_topological_sort
from .. import http
from ..http import request
NOMODULE_TEMPLATE = Template(u"""<!DOCTYPE html>
<html>
<head>
@ -162,3 +164,11 @@ class TestRunnerController(http.Controller):
for path in glob.glob(normalized_pattern):
# replace OS path separators (from join & normpath) by URI ones
yield path[len(root):].replace(os.path.sep, '/')
@http.route('/web/tests/set_session_value', type='json', auth="none")
def set_session_value(self, value):
request.session.some_test_value = value
@http.route('/web/tests/get_session_value', type='json', auth="none")
def get_session_value(self):
return request.session.some_test_value

View File

@ -114,7 +114,7 @@ class WebRequest(object):
threading.current_thread().dbname = self.db
if self.session.uid:
threading.current_thread().uid = self.session.uid
self.context = self.session.context
self.context = dict(self.session.context)
self.lang = self.context["lang"]
def _authenticate(self):
@ -275,13 +275,12 @@ class JsonRequest(WebRequest):
self.jsonp = jsonp
request = None
request_id = args.get('id')
if jsonp and self.httprequest.method == 'POST':
# jsonp 2 steps step1 POST: save call
self.init(args)
def handler():
self.session.jsonp_requests[request_id] = self.httprequest.form['r']
self.session.modified = True
headers=[('Content-Type', 'text/plain; charset=utf-8')]
r = werkzeug.wrappers.Response(request_id, headers=headers)
return r
@ -292,7 +291,6 @@ class JsonRequest(WebRequest):
request = args.get('r')
elif jsonp and request_id:
# jsonp 2 steps step2 GET: run and return result
self.init(args)
request = self.session.jsonp_requests.pop(request_id, "")
else:
# regular jsonrpc2
@ -576,6 +574,8 @@ class Model(object):
raise Exception("Trying to use Model with badly configured database or user.")
mod = request.registry.get(self.model)
if method.startswith('_'):
raise Exception("Access denied")
meth = getattr(mod, method)
cr = request.cr
result = meth(cr, request.uid, *args, **kw)
@ -608,7 +608,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
return self.__setitem__(k, v)
object.__setattr__(self, k, v)
def authenticate(self, db, login=None, password=None, env=None, uid=None):
def authenticate(self, db, login=None, password=None, uid=None):
"""
Authenticate the current user with the given db, login and password. If successful, store
the authentication parameters in the current session and request.
@ -617,7 +617,8 @@ class OpenERPSession(werkzeug.contrib.sessions.Session):
"""
if uid is None:
uid = openerp.netsvc.dispatch_rpc('common', 'authenticate', [db, login, password, env])
uid = openerp.netsvc.dispatch_rpc('common', 'authenticate', [db, login, password,
request.httprequest.environ])
else:
security.check(db, uid, password)
self.db = db
@ -946,13 +947,9 @@ class Root(object):
if httprequest.args.get('jsonp'):
return JsonRequest(httprequest)
content = httprequest.stream.read()
import cStringIO
httprequest.stream = cStringIO.StringIO(content)
try:
simplejson.loads(content)
if httprequest.headers["Content-Type"] == "application/json":
return JsonRequest(httprequest)
except:
else:
return HttpRequest(httprequest)
def load_addons(self):

File diff suppressed because it is too large Load Diff

9472
addons/web/static/lib/jquery/jquery.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,26 @@
/*
Copyright (c) 2013, Fabien Meghazi
Released under the MIT license
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// vim:set et fdm=syntax fdl=0 fdc=3 fdn=2:
//---------------------------------------------------------
// QWeb javascript

View File

@ -1,3 +1,26 @@
/*
Copyright (c) 2013, Fabien Meghazi
Released under the MIT license
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// TODO: trim support
// TODO: line number -> https://bugzilla.mozilla.org/show_bug.cgi?id=618650
// TODO: templates orverwritten could be called by t-call="__super__" ?

View File

@ -4,7 +4,7 @@
// Documentation: https://github.com/epeli/underscore.string
// Some code is borrowed from MooTools and Alexandru Marasteanu.
// Version 1.2.0
// Version 2.2.0rc
(function(root){
'use strict';
@ -12,12 +12,17 @@
// Defining helper functions.
var nativeTrim = String.prototype.trim;
var nativeTrimRight = String.prototype.trimRight;
var nativeTrimLeft = String.prototype.trimLeft;
var parseNumber = function(source) { return source * 1 || 0; };
var strRepeat = function(i, m) {
for (var o = []; m > 0; o[--m] = i) {}
return o.join('');
var strRepeat = function(str, qty, separator){
// ~~var — is the fastest available way to convert anything to Integer in javascript.
// We'll use it extensively in this lib.
str += ''; qty = ~~qty;
for (var repeat = []; qty > 0; repeat[--qty] = str) {}
return repeat.join(separator == null ? '' : separator);
};
var slice = function(a){
@ -25,20 +30,22 @@
};
var defaultToWhiteSpace = function(characters){
if (characters) {
return _s.escapeRegExp(characters);
if (characters != null) {
return '[' + _s.escapeRegExp(''+characters) + ']';
}
return '\\s';
};
var sArgs = function(method){
return function(){
var args = slice(arguments);
for(var i=0; i<args.length; i++)
args[i] = args[i] == null ? '' : '' + args[i];
return method.apply(null, args);
};
var escapeChars = {
lt: '<',
gt: '>',
quot: '"',
apos: "'",
amp: '&'
};
var reversedEscapeChars = {};
for(var key in escapeChars){ reversedEscapeChars[escapeChars[key]] = key; }
// sprintf() for JavaScript 0.7-beta1
// http://www.diveintojavascript.com/projects/javascript-sprintf
@ -168,213 +175,229 @@
var _s = {
VERSION: '1.2.0',
VERSION: '2.2.0rc',
isBlank: sArgs(function(str){
isBlank: function(str){
return (/^\s*$/).test(str);
}),
},
stripTags: sArgs(function(str){
return str.replace(/<\/?[^>]+>/ig, '');
}),
stripTags: function(str){
return (''+str).replace(/<\/?[^>]+>/g, '');
},
capitalize : sArgs(function(str) {
return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
}),
capitalize : function(str) {
str += '';
return str.charAt(0).toUpperCase() + str.substring(1);
},
chop: sArgs(function(str, step){
step = parseNumber(step) || str.length;
chop: function(str, step){
str = str+'';
step = ~~step || str.length;
var arr = [];
for (var i = 0; i < str.length;) {
for (var i = 0; i < str.length; i += step)
arr.push(str.slice(i,i + step));
i = i + step;
}
return arr;
}),
},
clean: sArgs(function(str){
return _s.strip(str.replace(/\s+/g, ' '));
}),
clean: function(str){
return _s.strip(str).replace(/\s+/g, ' ');
},
count: sArgs(function(str, substr){
var count = 0, index;
for (var i=0; i < str.length;) {
index = str.indexOf(substr, i);
index >= 0 && count++;
i = i + (index >= 0 ? index : 0) + substr.length;
}
return count;
}),
count: function(str, substr){
str += ''; substr += '';
return str.split(substr).length - 1;
},
chars: sArgs(function(str) {
return str.split('');
}),
chars: function(str) {
return (''+str).split('');
},
escapeHTML: sArgs(function(str) {
return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
.replace(/"/g, '&quot;').replace(/'/g, "&apos;");
}),
escapeHTML: function(str) {
return (''+str).replace(/[&<>"']/g, function(match){ return '&' + reversedEscapeChars[match] + ';'; });
},
unescapeHTML: sArgs(function(str) {
return str.replace(/&lt;/g, '<').replace(/&gt;/g, '>')
.replace(/&quot;/g, '"').replace(/&apos;/g, "'").replace(/&amp;/g, '&');
}),
unescapeHTML: function(str) {
return (''+str).replace(/\&([^;]+);/g, function(entity, entityCode){
var match;
if (entityCode in escapeChars) {
return escapeChars[entityCode];
} else if (match = entityCode.match(/^#x([\da-fA-F]+)$/)) {
return String.fromCharCode(parseInt(match[1], 16));
} else if (match = entityCode.match(/^#(\d+)$/)) {
return String.fromCharCode(~~match[1]);
} else {
return entity;
}
});
},
escapeRegExp: sArgs(function(str){
escapeRegExp: function(str){
// From MooTools core 1.2.4
return str.replace(/([-.*+?^${}()|[\]\/\\])/g, '\\$1');
}),
},
insert: sArgs(function(str, i, substr){
var arr = str.split('');
arr.splice(parseNumber(i), 0, substr);
insert: function(str, i, substr){
var arr = _s.chars(str);
arr.splice(~~i, 0, ''+substr);
return arr.join('');
}),
},
include: sArgs(function(str, needle){
return str.indexOf(needle) !== -1;
}),
include: function(str, needle){
return !!~(''+str).indexOf(needle);
},
join: sArgs(function(sep) {
join: function() {
var args = slice(arguments);
return args.join(args.shift());
}),
},
lines: sArgs(function(str) {
return str.split("\n");
}),
lines: function(str) {
return (''+str).split("\n");
},
reverse: sArgs(function(str){
return Array.prototype.reverse.apply(String(str).split('')).join('');
}),
reverse: function(str){
return _s.chars(str).reverse().join('');
},
splice: sArgs(function(str, i, howmany, substr){
var arr = str.split('');
arr.splice(parseNumber(i), parseNumber(howmany), substr);
splice: function(str, i, howmany, substr){
var arr = _s.chars(str);
arr.splice(~~i, ~~howmany, substr);
return arr.join('');
}),
},
startsWith: sArgs(function(str, starts){
startsWith: function(str, starts){
str += ''; starts += '';
return str.length >= starts.length && str.substring(0, starts.length) === starts;
}),
},
endsWith: sArgs(function(str, ends){
endsWith: function(str, ends){
str += ''; ends += '';
return str.length >= ends.length && str.substring(str.length - ends.length) === ends;
}),
},
succ: sArgs(function(str){
var arr = str.split('');
succ: function(str){
str += '';
var arr = _s.chars(str);
arr.splice(str.length-1, 1, String.fromCharCode(str.charCodeAt(str.length-1) + 1));
return arr.join('');
}),
},
titleize: sArgs(function(str){
var arr = str.split(' '),
word;
for (var i=0; i < arr.length; i++) {
word = arr[i].split('');
if(typeof word[0] !== 'undefined') word[0] = word[0].toUpperCase();
i+1 === arr.length ? arr[i] = word.join('') : arr[i] = word.join('') + ' ';
}
return arr.join('');
}),
titleize: function(str){
return (''+str).replace(/\b./g, function(ch){ return ch.toUpperCase(); });
},
camelize: sArgs(function(str){
return _s.trim(str).replace(/(\-|_|\s)+(.)?/g, function(match, separator, chr) {
return chr ? chr.toUpperCase() : '';
camelize: function(str){
return _s.trim(str).replace(/[-_\s]+(.)?/g, function(match, chr){
return chr && chr.toUpperCase();
});
}),
},
underscored: function(str){
return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/\-|\s+/g, '_').toLowerCase();
return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase();
},
dasherize: function(str){
return _s.trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1-$2').replace(/^([A-Z]+)/, '-$1').replace(/\_|\s+/g, '-').toLowerCase();
return _s.trim(str).replace(/[_\s]+/g, '-').replace(/([A-Z])/g, '-$1').replace(/-+/g, '-').toLowerCase();
},
classify: function(str){
str += '';
return _s.titleize(str.replace(/_/g, ' ')).replace(/\s/g, '')
},
humanize: function(str){
return _s.capitalize(this.underscored(str).replace(/_id$/,'').replace(/_/g, ' '));
},
trim: sArgs(function(str, characters){
if (!characters && nativeTrim) {
return nativeTrim.call(str);
trim: function(str, characters){
str += '';
if (!characters && nativeTrim) { return nativeTrim.call(str); }
characters = defaultToWhiteSpace(characters);
return str.replace(new RegExp('\^' + characters + '+|' + characters + '+$', 'g'), '');
},
ltrim: function(str, characters){
str+='';
if (!characters && nativeTrimLeft) {
return nativeTrimLeft.call(str);
}
characters = defaultToWhiteSpace(characters);
return str.replace(new RegExp('\^[' + characters + ']+|[' + characters + ']+$', 'g'), '');
}),
return str.replace(new RegExp('^' + characters + '+'), '');
},
ltrim: sArgs(function(str, characters){
rtrim: function(str, characters){
str+='';
if (!characters && nativeTrimRight) {
return nativeTrimRight.call(str);
}
characters = defaultToWhiteSpace(characters);
return str.replace(new RegExp('\^[' + characters + ']+', 'g'), '');
}),
return str.replace(new RegExp(characters + '+$'), '');
},
rtrim: sArgs(function(str, characters){
characters = defaultToWhiteSpace(characters);
return str.replace(new RegExp('[' + characters + ']+$', 'g'), '');
}),
truncate: sArgs(function(str, length, truncateStr){
truncateStr = truncateStr || '...';
length = parseNumber(length);
return str.length > length ? str.slice(0,length) + truncateStr : str;
}),
truncate: function(str, length, truncateStr){
str += ''; truncateStr = truncateStr || '...';
length = ~~length;
return str.length > length ? str.slice(0, length) + truncateStr : str;
},
/**
* _s.prune: a more elegant version of truncate
* prune extra chars, never leaving a half-chopped word.
* @author github.com/sergiokas
*/
prune: sArgs(function(str, length, pruneStr){
pruneStr = pruneStr || '...';
length = parseNumber(length);
var pruned = '';
prune: function(str, length, pruneStr){
str += ''; length = ~~length;
pruneStr = pruneStr != null ? ''+pruneStr : '...';
var pruned, borderChar, template = str.replace(/\W/g, function(ch){
return (ch.toUpperCase() !== ch.toLowerCase()) ? 'A' : ' ';
});
borderChar = template.charAt(length);
pruned = template.slice(0, length);
// Check if we're in the middle of a word
if( str.substring(length-1, length+1).search(/^\w\w$/) === 0 )
pruned = _s.rtrim(str.slice(0,length).replace(/([\W][\w]*)$/,''));
else
pruned = _s.rtrim(str.slice(0,length));
pruned = pruned.replace(/\W+$/,'');
return (pruned.length+pruneStr.length>str.length) ? str : pruned + pruneStr;
}),
words: function(str, delimiter) {
return String(str).split(delimiter || " ");
if (borderChar && borderChar.match(/\S/))
pruned = pruned.replace(/\s\S+$/, '');
pruned = _s.rtrim(pruned);
return (pruned+pruneStr).length > str.length ? str : str.substring(0, pruned.length)+pruneStr;
},
pad: sArgs(function(str, length, padStr, type) {
var padding = '',
padlen = 0;
words: function(str, delimiter) {
return _s.trim(str, delimiter).split(delimiter || /\s+/);
},
length = parseNumber(length);
pad: function(str, length, padStr, type) {
str += '';
var padlen = 0;
if (!padStr) { padStr = ' '; }
else if (padStr.length > 1) { padStr = padStr.charAt(0); }
length = ~~length;
if (!padStr) {
padStr = ' ';
} else if (padStr.length > 1) {
padStr = padStr.charAt(0);
}
switch(type) {
case 'right':
padlen = (length - str.length);
padding = strRepeat(padStr, padlen);
str = str+padding;
break;
return str + strRepeat(padStr, padlen);
case 'both':
padlen = (length - str.length);
padding = {
'left' : strRepeat(padStr, Math.ceil(padlen/2)),
'right': strRepeat(padStr, Math.floor(padlen/2))
};
str = padding.left+str+padding.right;
break;
return strRepeat(padStr, Math.ceil(padlen/2)) +
str +
strRepeat(padStr, Math.floor(padlen/2));
default: // 'left'
padlen = (length - str.length);
padding = strRepeat(padStr, padlen);;
str = padding+str;
return strRepeat(padStr, padlen) + str;
}
return str;
}),
},
lpad: function(str, length, padStr) {
return _s.pad(str, length, padStr);
@ -396,41 +419,76 @@
},
toNumber: function(str, decimals) {
var num = parseNumber(parseNumber(str).toFixed(parseNumber(decimals)));
return (!(num === 0 && (str !== "0" && str !== 0))) ? num : Number.NaN;
str += '';
var num = parseNumber(parseNumber(str).toFixed(~~decimals));
return num === 0 && !str.match(/^0+$/) ? Number.NaN : num;
},
strRight: sArgs(function(sourceStr, sep){
var pos = (!sep) ? -1 : sourceStr.indexOf(sep);
return (pos != -1) ? sourceStr.slice(pos+sep.length, sourceStr.length) : sourceStr;
}),
strRight: function(str, sep){
str += ''; sep = sep != null ? ''+sep : sep;
var pos = !sep ? -1 : str.indexOf(sep);
return ~pos ? str.slice(pos+sep.length, str.length) : str;
},
strRightBack: sArgs(function(sourceStr, sep){
var pos = (!sep) ? -1 : sourceStr.lastIndexOf(sep);
return (pos != -1) ? sourceStr.slice(pos+sep.length, sourceStr.length) : sourceStr;
}),
strRightBack: function(str, sep){
str += ''; sep = sep != null ? ''+sep : sep;
var pos = !sep ? -1 : str.lastIndexOf(sep);
return ~pos ? str.slice(pos+sep.length, str.length) : str;
},
strLeft: sArgs(function(sourceStr, sep){
var pos = (!sep) ? -1 : sourceStr.indexOf(sep);
return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
}),
strLeft: function(str, sep){
str += ''; sep = sep != null ? ''+sep : sep;
var pos = !sep ? -1 : str.indexOf(sep);
return ~pos ? str.slice(0, pos) : str;
},
strLeftBack: sArgs(function(sourceStr, sep){
var pos = sourceStr.lastIndexOf(sep);
return (pos != -1) ? sourceStr.slice(0, pos) : sourceStr;
}),
strLeftBack: function(str, sep){
str += ''; sep = sep != null ? ''+sep : sep;
var pos = str.lastIndexOf(sep);
return ~pos ? str.slice(0, pos) : str;
},
toSentence: function(array, separator, lastSeparator) {
separator || (separator = ', ');
lastSeparator || (lastSeparator = ' and ');
var length = array.length, str = '';
for (var i = 0; i < length; i++) {
str += array[i];
if (i === (length - 2)) { str += lastSeparator; }
else if (i < (length - 1)) { str += separator; }
}
return str;
},
slugify: function(str) {
var from = "ąàáäâãćęèéëêìíïîłńòóöôõùúüûñçżź",
to = "aaaaaaceeeeeiiiilnooooouuuunczz",
regex = new RegExp(defaultToWhiteSpace(from), 'g');
str = (''+str).toLowerCase();
str = str.replace(regex, function(ch){
var index = from.indexOf(ch);
return to.charAt(index) || '-';
});
return _s.trim(str.replace(/[^\w\s-]/g, '').replace(/[-\s]+/g, '-'), '-');
},
exports: function() {
var result = {};
for (var prop in this) {
if (!this.hasOwnProperty(prop) || prop == 'include' || prop == 'contains' || prop == 'reverse') continue;
if (!this.hasOwnProperty(prop) || ~_s.words('include contains reverse').indexOf(prop)) continue;
result[prop] = this[prop];
}
return result;
}
},
repeat: strRepeat
};
// Aliases
@ -439,8 +497,8 @@
_s.lstrip = _s.ltrim;
_s.rstrip = _s.rtrim;
_s.center = _s.lrpad;
_s.ljust = _s.lpad;
_s.rjust = _s.rpad;
_s.rjust = _s.lpad;
_s.ljust = _s.rpad;
_s.contains = _s.include;
// CommonJS module is defined
@ -451,18 +509,17 @@
}
exports._s = _s;
// Integrate with Underscore.js
} else if (typeof root._ !== 'undefined') {
// root._.mixin(_s);
root._.string = _s;
root._.str = root._.string;
// Or define it
} else if (typeof define === 'function' && define.amd) {
// Register as a named module with AMD.
define('underscore.string', function() {
return _s;
});
} else {
root._ = {
string: _s,
str: _s
};
// Integrate with Underscore.js if defined
// or create our own underscore object.
root._ = root._ || {};
root._.string = root._.str = _s;
}
}(this || window));

View File

@ -1,31 +0,0 @@
// Underscore.js 1.3.1
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function(){function q(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return a==String(c);case "[object Number]":return a!=+a?c!=+c:a==0?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&q(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&q(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,
h)&&!f--)break;g=!f}}d.pop();return g}var r=this,G=r._,n={},k=Array.prototype,o=Object.prototype,i=k.slice,H=k.unshift,l=o.toString,I=o.hasOwnProperty,w=k.forEach,x=k.map,y=k.reduce,z=k.reduceRight,A=k.filter,B=k.every,C=k.some,p=k.indexOf,D=k.lastIndexOf,o=Array.isArray,J=Object.keys,s=Function.prototype.bind,b=function(a){return new m(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else r._=b;b.VERSION="1.3.1";var j=b.each=
b.forEach=function(a,c,d){if(a!=null)if(w&&a.forEach===w)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===n)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===n)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(x&&a.map===x)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==
null&&(a=[]);if(y&&a.reduce===y)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(z&&a.reduceRight===z)return e&&(c=b.bind(c,e)),f?a.reduceRight(c,d):a.reduceRight(c);var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=
function(a,c,b){var e;E(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(A&&a.filter===A)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(B&&a.every===B)return a.every(c,b);j(a,function(a,g,h){if(!(e=
e&&c.call(b,a,g,h)))return n});return e};var E=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(C&&a.some===C)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return n});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return p&&a.indexOf===p?a.indexOf(c)!=-1:b=E(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,computed:b})});
return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){f==0?b[0]=a:(d=Math.floor(Math.random()*(f+1)),b[f]=b[d],b[d]=a)});return b};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,
c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=
b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},[]);
return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1));return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,
d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(p&&a.indexOf===p)return a.indexOf(c);for(d=0,e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(D&&a.lastIndexOf===D)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};
var F=function(){};b.bind=function(a,c){var d,e;if(a.bind===s&&s)return s.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));F.prototype=a.prototype;var b=new F,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,
c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true:
a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};
b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=J||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.defaults=function(a){j(i.call(arguments,
1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return q(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=o||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};
b.isArguments=function(a){return l.call(a)=="[object Arguments]"};if(!b.isArguments(arguments))b.isArguments=function(a){return!(!a||!b.has(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};
b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,b){return I.call(a,b)};b.noConflict=function(){r._=G;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.mixin=function(a){j(b.functions(a),
function(c){K(c,b[c]=a[c])})};var L=0;b.uniqueId=function(a){var b=L++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var t=/.^/,u=function(a){return a.replace(/\\\\/g,"\\").replace(/\\'/g,"'")};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape||t,function(a,b){return"',_.escape("+
u(b)+"),'"}).replace(d.interpolate||t,function(a,b){return"',"+u(b)+",'"}).replace(d.evaluate||t,function(a,b){return"');"+u(b).replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e.call(this,a,b)}};b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var v=function(a,c){return c?b(a).chain():a},K=function(a,c){m.prototype[a]=
function(){var a=i.call(arguments);H.call(a,this._wrapped);return v(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return v(d,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return v(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=
true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);

View File

@ -1,14 +0,0 @@
(function(k){var o=String.prototype.trim,l=function(a,b){for(var c=[];b>0;c[--b]=a);return c.join("")},d=function(a){return function(){for(var b=Array.prototype.slice.call(arguments),c=0;c<b.length;c++)b[c]=b[c]==null?"":""+b[c];return a.apply(null,b)}},m=function(){function a(a){return Object.prototype.toString.call(a).slice(8,-1).toLowerCase()}var b=function(){b.cache.hasOwnProperty(arguments[0])||(b.cache[arguments[0]]=b.parse(arguments[0]));return b.format.call(null,b.cache[arguments[0]],arguments)};
b.format=function(b,n){var e=1,d=b.length,f="",j=[],h,i,g,k;for(h=0;h<d;h++)if(f=a(b[h]),f==="string")j.push(b[h]);else if(f==="array"){g=b[h];if(g[2]){f=n[e];for(i=0;i<g[2].length;i++){if(!f.hasOwnProperty(g[2][i]))throw m('[_.sprintf] property "%s" does not exist',g[2][i]);f=f[g[2][i]]}}else f=g[1]?n[g[1]]:n[e++];if(/[^s]/.test(g[8])&&a(f)!="number")throw m("[_.sprintf] expecting number but found %s",a(f));switch(g[8]){case "b":f=f.toString(2);break;case "c":f=String.fromCharCode(f);break;case "d":f=
parseInt(f,10);break;case "e":f=g[7]?f.toExponential(g[7]):f.toExponential();break;case "f":f=g[7]?parseFloat(f).toFixed(g[7]):parseFloat(f);break;case "o":f=f.toString(8);break;case "s":f=(f=String(f))&&g[7]?f.substring(0,g[7]):f;break;case "u":f=Math.abs(f);break;case "x":f=f.toString(16);break;case "X":f=f.toString(16).toUpperCase()}f=/[def]/.test(g[8])&&g[3]&&f>=0?"+"+f:f;i=g[4]?g[4]=="0"?"0":g[4].charAt(1):" ";k=g[6]-String(f).length;i=g[6]?l(i,k):"";j.push(g[5]?f+i:i+f)}return j.join("")};b.cache=
{};b.parse=function(a){for(var b=[],e=[],d=0;a;){if((b=/^[^\x25]+/.exec(a))!==null)e.push(b[0]);else if((b=/^\x25{2}/.exec(a))!==null)e.push("%");else if((b=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(a))!==null){if(b[2]){d|=1;var f=[],j=b[2],h=[];if((h=/^([a-z_][a-z_\d]*)/i.exec(j))!==null)for(f.push(h[1]);(j=j.substring(h[0].length))!=="";)if((h=/^\.([a-z_][a-z_\d]*)/i.exec(j))!==null)f.push(h[1]);else if((h=/^\[(\d+)\]/.exec(j))!==null)f.push(h[1]);
else throw"[_.sprintf] huh?";else throw"[_.sprintf] huh?";b[2]=f}else d|=2;if(d===3)throw"[_.sprintf] mixing positional and named placeholders is not (yet) supported";e.push(b)}else throw"[_.sprintf] huh?";a=a.substring(b[0].length)}return e};return b}(),e={VERSION:"1.2.0",isBlank:d(function(a){return/^\s*$/.test(a)}),stripTags:d(function(a){return a.replace(/<\/?[^>]+>/ig,"")}),capitalize:d(function(a){return a.charAt(0).toUpperCase()+a.substring(1).toLowerCase()}),chop:d(function(a,b){for(var b=
b*1||0||a.length,c=[],e=0;e<a.length;)c.push(a.slice(e,e+b)),e+=b;return c}),clean:d(function(a){return e.strip(a.replace(/\s+/g," "))}),count:d(function(a,b){for(var c=0,e,d=0;d<a.length;)e=a.indexOf(b,d),e>=0&&c++,d=d+(e>=0?e:0)+b.length;return c}),chars:d(function(a){return a.split("")}),escapeHTML:d(function(a){return a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}),unescapeHTML:d(function(a){return a.replace(/&lt;/g,"<").replace(/&gt;/g,
">").replace(/&quot;/g,'"').replace(/&apos;/g,"'").replace(/&amp;/g,"&")}),escapeRegExp:d(function(a){return a.replace(/([-.*+?^${}()|[\]\/\\])/g,"\\$1")}),insert:d(function(a,b,c){a=a.split("");a.splice(b*1||0,0,c);return a.join("")}),include:d(function(a,b){return a.indexOf(b)!==-1}),join:d(function(a){var b=Array.prototype.slice.call(arguments);return b.join(b.shift())}),lines:d(function(a){return a.split("\n")}),reverse:d(function(a){return Array.prototype.reverse.apply(String(a).split("")).join("")}),
splice:d(function(a,b,c,e){a=a.split("");a.splice(b*1||0,c*1||0,e);return a.join("")}),startsWith:d(function(a,b){return a.length>=b.length&&a.substring(0,b.length)===b}),endsWith:d(function(a,b){return a.length>=b.length&&a.substring(a.length-b.length)===b}),succ:d(function(a){var b=a.split("");b.splice(a.length-1,1,String.fromCharCode(a.charCodeAt(a.length-1)+1));return b.join("")}),titleize:d(function(a){for(var a=a.split(" "),b,c=0;c<a.length;c++)b=a[c].split(""),typeof b[0]!=="undefined"&&(b[0]=
b[0].toUpperCase()),c+1===a.length?a[c]=b.join(""):a[c]=b.join("")+" ";return a.join("")}),camelize:d(function(a){return e.trim(a).replace(/(\-|_|\s)+(.)?/g,function(a,c,e){return e?e.toUpperCase():""})}),underscored:function(a){return e.trim(a).replace(/([a-z\d])([A-Z]+)/g,"$1_$2").replace(/\-|\s+/g,"_").toLowerCase()},dasherize:function(a){return e.trim(a).replace(/([a-z\d])([A-Z]+)/g,"$1-$2").replace(/^([A-Z]+)/,"-$1").replace(/\_|\s+/g,"-").toLowerCase()},humanize:function(a){return e.capitalize(this.underscored(a).replace(/_id$/,
"").replace(/_/g," "))},trim:d(function(a,b){if(!b&&o)return o.call(a);b=b?e.escapeRegExp(b):"\\s";return a.replace(RegExp("^["+b+"]+|["+b+"]+$","g"),"")}),ltrim:d(function(a,b){b=b?e.escapeRegExp(b):"\\s";return a.replace(RegExp("^["+b+"]+","g"),"")}),rtrim:d(function(a,b){b=b?e.escapeRegExp(b):"\\s";return a.replace(RegExp("["+b+"]+$","g"),"")}),truncate:d(function(a,b,c){b=b*1||0;return a.length>b?a.slice(0,b)+(c||"..."):a}),prune:d(function(a,b,c){var c=c||"...",b=b*1||0,d="",d=a.substring(b-
1,b+1).search(/^\w\w$/)===0?e.rtrim(a.slice(0,b).replace(/([\W][\w]*)$/,"")):e.rtrim(a.slice(0,b)),d=d.replace(/\W+$/,"");return d.length+c.length>a.length?a:d+c}),words:function(a,b){return String(a).split(b||" ")},pad:d(function(a,b,c,e){var d="",d=0,b=b*1||0;c?c.length>1&&(c=c.charAt(0)):c=" ";switch(e){case "right":d=b-a.length;d=l(c,d);a+=d;break;case "both":d=b-a.length;d={left:l(c,Math.ceil(d/2)),right:l(c,Math.floor(d/2))};a=d.left+a+d.right;break;default:d=b-a.length,d=l(c,d),a=d+a}return a}),
lpad:function(a,b,c){return e.pad(a,b,c)},rpad:function(a,b,c){return e.pad(a,b,c,"right")},lrpad:function(a,b,c){return e.pad(a,b,c,"both")},sprintf:m,vsprintf:function(a,b){b.unshift(a);return m.apply(null,b)},toNumber:function(a,b){var c;c=(a*1||0).toFixed(b*1||0)*1||0;return!(c===0&&a!=="0"&&a!==0)?c:Number.NaN},strRight:d(function(a,b){var c=!b?-1:a.indexOf(b);return c!=-1?a.slice(c+b.length,a.length):a}),strRightBack:d(function(a,b){var c=!b?-1:a.lastIndexOf(b);return c!=-1?a.slice(c+b.length,
a.length):a}),strLeft:d(function(a,b){var c=!b?-1:a.indexOf(b);return c!=-1?a.slice(0,c):a}),strLeftBack:d(function(a,b){var c=a.lastIndexOf(b);return c!=-1?a.slice(0,c):a}),exports:function(){var a={},b;for(b in this)if(this.hasOwnProperty(b)&&!(b=="include"||b=="contains"||b=="reverse"))a[b]=this[b];return a}};e.strip=e.trim;e.lstrip=e.ltrim;e.rstrip=e.rtrim;e.center=e.lrpad;e.ljust=e.lpad;e.rjust=e.rpad;e.contains=e.include;if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)module.exports=
e;exports._s=e}else typeof k._!=="undefined"?(k._.string=e,k._.str=k._.string):k._={string:e,str:e}})(this||window);

View File

@ -7,6 +7,9 @@
* @namespace openerp
*/
(function() {
// copy everything in the openerp namespace to openerp.web
openerp.web = _.clone(openerp);
var inited = false;
_.extend(openerp, {
@ -30,13 +33,11 @@
if (modules === null) {
modules = [];
}
modules = _.without(modules, "web");
if (inited)
throw new Error("OpenERP was already inited");
inited = true;
init_web_modules();
for(var i=0; i < modules.length; i++) {
if (modules[i] === "web")
continue;
var fct = openerp[modules[i]];
if (typeof(fct) === "function") {
openerp[modules[i]] = {};
@ -50,22 +51,5 @@
return openerp;
}
});
/*---------------------------------------------------------
* OpenERP Web web module split
*---------------------------------------------------------*/
function init_web_modules() {
var files = ["pyeval", "corelib","coresetup","dates","formats","chrome","data","views","search","list","form","list_editable","web_mobile","view_tree","data_export","data_import"];
for(var i=0; i<files.length; i++) {
var fct = openerp.web[files[i]];
if(typeof(fct) === "function") {
openerp.web[files[i]] = {};
for (var k in fct) {
openerp.web[files[i]][k] = fct[k];
}
fct(openerp, openerp.web[files[i]]);
}
}
}
})();

View File

@ -1,7 +1,11 @@
/*---------------------------------------------------------
* OpenERP Web chrome
*---------------------------------------------------------*/
openerp.web.chrome = function(instance) {
(function() {
var instance = openerp;
openerp.web.chrome = {};
var QWeb = instance.web.qweb,
_t = instance.web._t;
@ -1619,6 +1623,6 @@ instance.web.embed = function (origin, dbname, login, key, action, options) {
client.insertAfter(currentScript);
};
};
})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -1,10 +1,9 @@
/*---------------------------------------------------------
* OpenERP Web core
*--------------------------------------------------------*/
var console;
if (!console) {
(function() {
if (typeof(console) === "undefined") {
// Even IE9 only exposes console object if debug window opened
console = {};
window.console = {};
('log error debug info warn assert clear dir dirxml trace group'
+ ' groupCollapsed groupEnd time timeEnd profile profileEnd count'
+ ' exception').split(/\s+/).forEach(function(property) {
@ -12,29 +11,230 @@ if (!console) {
});
}
openerp.web.coresetup = function(instance) {
// shim provided by mozilla for function.bind
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
/** Session openerp specific RPC class */
instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Session# */{
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
var instance = openerp;
openerp.web.core = {};
var ControllerMixin = {
/**
* Informs the action manager to do an action. This supposes that
* the action manager can be found amongst the ancestors of the current widget.
* If that's not the case this method will simply return `false`.
*/
do_action: function() {
var parent = this.getParent();
if (parent) {
return parent.do_action.apply(parent, arguments);
}
return false;
},
do_notify: function() {
if (this.getParent()) {
return this.getParent().do_notify.apply(this,arguments);
}
return false;
},
do_warn: function() {
if (this.getParent()) {
return this.getParent().do_warn.apply(this,arguments);
}
return false;
},
rpc: function(url, data, options) {
return this.alive(openerp.session.rpc(url, data, options));
}
};
/**
A class containing common utility methods useful when working with OpenERP as well as the PropertiesMixin.
*/
openerp.web.Controller = openerp.web.Class.extend(openerp.web.PropertiesMixin, ControllerMixin, {
/**
* Constructs the object and sets its parent if a parent is given.
*
* @param {openerp.web.Controller} parent Binds the current instance to the given Controller instance.
* When that controller is destroyed by calling destroy(), the current instance will be
* destroyed too. Can be null.
*/
init: function(parent) {
openerp.web.PropertiesMixin.init.call(this);
this.setParent(parent);
this.session = openerp.session;
},
});
openerp.web.Widget.include(_.extend({}, ControllerMixin, {
init: function() {
this._super.apply(this, arguments);
this.session = openerp.session;
},
}));
instance.web.Registry = instance.web.Class.extend({
/**
* Stores a mapping of arbitrary key (strings) to object paths (as strings
* as well).
*
* Resolves those paths at query time in order to always fetch the correct
* object, even if those objects have been overloaded/replaced after the
* registry was created.
*
* An object path is simply a dotted name from the instance root to the
* object pointed to (e.g. ``"instance.web.Session"`` for an OpenERP
* session object).
*
* @constructs instance.web.Registry
* @param {Object} mapping a mapping of keys to object-paths
*/
init: function (mapping) {
this.parent = null;
this.map = mapping || {};
},
/**
* Retrieves the object matching the provided key string.
*
* @param {String} key the key to fetch the object for
* @param {Boolean} [silent_error=false] returns undefined if the key or object is not found, rather than throwing an exception
* @returns {Class} the stored class, to initialize or null if not found
*/
get_object: function (key, silent_error) {
var path_string = this.map[key];
if (path_string === undefined) {
if (this.parent) {
return this.parent.get_object(key, silent_error);
}
if (silent_error) { return void 'nooo'; }
return null;
}
var object_match = instance;
var path = path_string.split('.');
// ignore first section
for(var i=1; i<path.length; ++i) {
object_match = object_match[path[i]];
if (object_match === undefined) {
if (silent_error) { return void 'noooooo'; }
return null;
}
}
return object_match;
},
/**
* Checks if the registry contains an object mapping for this key.
*
* @param {String} key key to look for
*/
contains: function (key) {
if (key === undefined) { return false; }
if (key in this.map) {
return true;
}
if (this.parent) {
return this.parent.contains(key);
}
return false;
},
/**
* Tries a number of keys, and returns the first object matching one of
* the keys.
*
* @param {Array} keys a sequence of keys to fetch the object for
* @returns {Class} the first class found matching an object
*/
get_any: function (keys) {
for (var i=0; i<keys.length; ++i) {
var key = keys[i];
if (!this.contains(key)) {
continue;
}
return this.get_object(key);
}
return null;
},
/**
* Adds a new key and value to the registry.
*
* This method can be chained.
*
* @param {String} key
* @param {String} object_path fully qualified dotted object path
* @returns {instance.web.Registry} itself
*/
add: function (key, object_path) {
this.map[key] = object_path;
return this;
},
/**
* Creates and returns a copy of the current mapping, with the provided
* mapping argument added in (replacing existing keys if needed)
*
* Parent and child remain linked, a new key in the parent (which is not
* overwritten by the child) will appear in the child.
*
* @param {Object} [mapping={}] a mapping of keys to object-paths
*/
extend: function (mapping) {
var child = new instance.web.Registry(mapping);
child.parent = this;
return child;
},
/**
* @deprecated use Registry#extend
*/
clone: function (mapping) {
console.warn('Registry#clone is deprecated, use Registry#extend');
return this.extend(mapping);
}
});
instance.web.py_eval = function(expr, context) {
return py.eval(expr, _.extend({}, context || {}, {"true": true, "false": false, "null": null}));
};
/*
Some retro-compatibility.
*/
instance.web.JsonRPC = instance.web.Session;
/** Session openerp specific RPC class */
instance.web.Session.include( /** @lends instance.web.Session# */{
init: function() {
this._super.apply(this, arguments);
this.debug = ($.deparam($.param.querystring()).debug !== undefined);
// TODO: session store in cookie should be optional
this.name = instance._session_id;
this.qweb_mutex = new $.Mutex();
},
rpc: function(url, params, options) {
return this._super(url, params, options);
},
/**
* Setup a sessionm
*/
session_bind: function(origin) {
if (!_.isUndefined(this.origin)) {
if (this.origin === origin) {
return $.when();
}
throw new Error('Session already bound to ' + this.origin);
}
var self = this;
this.setup(origin);
instance.web.qweb.default_dict['_s'] = this.origin;
@ -69,33 +269,6 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
);
});
},
/**
* (re)loads the content of a session: db name, username, user id, session
* context and status of the support contract
*
* @returns {$.Deferred} deferred indicating the session is done reloading
*/
session_reload: function () {
var self = this;
var def = $.when();
if (this.override_session) {
if (! this.session_id) {
def = this.rpc("/gen_session_id", {}).then(function(result) {
self.session_id = result;
});
}
} else {
this.session_id = this.get_cookie('session_id');
}
return def.then(function() {
return self.rpc("/web/session/get_session_info", {});
}).then(function(result) {
// If immediately follows a login (triggered by trying to restore
// an invalid session or no session at all), refresh session data
// (should not change, but just in case...)
_.extend(self, result);
});
},
session_is_valid: function() {
var db = $.deparam.querystring().db;
if (db && this.db !== db) {
@ -106,15 +279,9 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
/**
* The session is validated either by login or by restoration of a previous session
*/
session_authenticate: function(db, login, password, _volatile) {
session_authenticate: function() {
var self = this;
var base_location = document.location.protocol + '//' + document.location.host;
var params = { db: db, login: login, password: password, base_location: base_location };
return this.rpc("/web/session/authenticate", params).then(function(result) {
if (!result.uid) {
return $.Deferred().reject();
}
_.extend(self, result);
return $.when(this._super.apply(this, arguments)).then(function() {
return self.load_modules();
});
},
@ -193,10 +360,7 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
});
},
load_translations: function() {
var params = { mods: this.module_list, lang: this.user_context.lang };
return this.rpc('/web/webclient/translations', params).done(function(trans) {
instance.web._t.database.set_bundle(trans);
});
return instance.web._t.database.load_translations(this, this.module_list, this.user_context.lang);
},
load_css: function (files) {
var self = this;
@ -412,58 +576,13 @@ instance.web.Bus = instance.web.Class.extend(instance.web.EventDispatcherMixin,
});
instance.web.bus = new instance.web.Bus();
/** OpenERP Translations */
instance.web.TranslationDataBase = instance.web.Class.extend(/** @lends instance.web.TranslationDataBase# */{
/**
* @constructs instance.web.TranslationDataBase
* @extends instance.web.Class
*/
init: function() {
this.db = {};
this.parameters = {"direction": 'ltr',
"date_format": '%m/%d/%Y',
"time_format": '%H:%M:%S',
"grouping": [],
"decimal_point": ".",
"thousands_sep": ","};
},
instance.web.TranslationDataBase.include({
set_bundle: function(translation_bundle) {
var self = this;
this.db = {};
var modules = _.keys(translation_bundle.modules);
modules.sort();
if (_.include(modules, "web")) {
modules = ["web"].concat(_.without(modules, "web"));
}
_.each(modules, function(name) {
self.add_module_translation(translation_bundle.modules[name]);
});
this._super(translation_bundle);
if (translation_bundle.lang_parameters) {
this.parameters = translation_bundle.lang_parameters;
this.parameters.grouping = py.eval(
this.parameters.grouping);
this.parameters.grouping = py.eval(this.parameters.grouping);
}
},
add_module_translation: function(mod) {
var self = this;
_.each(mod.messages, function(message) {
self.db[message.id] = message.string;
});
},
build_translation_function: function() {
var self = this;
var fcnt = function(str) {
var tmp = self.get(str);
return tmp === undefined ? str : tmp;
};
fcnt.database = this;
return fcnt;
},
get: function(key) {
if (this.db[key])
return this.db[key];
return undefined;
}
});
/** Custom jQuery plugins */
@ -494,21 +613,7 @@ $.fn.openerpBounce = function() {
};
/** Jquery extentions */
$.Mutex = (function() {
function Mutex() {
this.def = $.Deferred().resolve();
}
Mutex.prototype.exec = function(action) {
var current = this.def;
var next = this.def = $.Deferred();
return current.then(function() {
return $.when(action()).always(function() {
next.resolve();
});
});
};
return Mutex;
})();
$.Mutex = openerp.Mutex;
$.async_when = function() {
var async = false;
@ -548,8 +653,6 @@ $.async_when = function() {
/** Setup default session */
instance.session = new instance.web.Session();
/** Configure default qweb */
instance.web._t = new instance.web.TranslationDataBase().build_translation_function();
/**
* Lazy translation function, only performs the translation when actually
* printed (e.g. inserted into a template)
@ -566,7 +669,6 @@ instance.web._lt = function (s) {
};
instance.web.qweb.debug = instance.session.debug;
_.extend(instance.web.qweb.default_dict, {
'_t' : instance.web._t,
'__debug__': instance.session.debug,
});
instance.web.qweb.preprocess_node = function() {
@ -707,6 +809,6 @@ instance.web.unblockUI = function() {
*/
instance.web.client_actions = new instance.web.Registry();
};
})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -1,408 +0,0 @@
/*
* Copyright (c) 2012, OpenERP S.A.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
openerp.web.corelib = function(instance) {
var ControllerMixin = {
/**
* Informs the action manager to do an action. This supposes that
* the action manager can be found amongst the ancestors of the current widget.
* If that's not the case this method will simply return `false`.
*/
do_action: function() {
var parent = this.getParent();
if (parent) {
return parent.do_action.apply(parent, arguments);
}
return false;
},
do_notify: function() {
if (this.getParent()) {
return this.getParent().do_notify.apply(this,arguments);
}
return false;
},
do_warn: function() {
if (this.getParent()) {
return this.getParent().do_warn.apply(this,arguments);
}
return false;
},
rpc: function(url, data, options) {
return this.alive(openerp.session.rpc(url, data, options));
}
};
/**
A class containing common utility methods useful when working with OpenERP as well as the PropertiesMixin.
*/
openerp.web.Controller = openerp.web.Class.extend(openerp.web.PropertiesMixin, ControllerMixin, {
/**
* Constructs the object and sets its parent if a parent is given.
*
* @param {openerp.web.Controller} parent Binds the current instance to the given Controller instance.
* When that controller is destroyed by calling destroy(), the current instance will be
* destroyed too. Can be null.
*/
init: function(parent) {
openerp.web.PropertiesMixin.init.call(this);
this.setParent(parent);
this.session = openerp.session;
},
});
openerp.web.Widget.include(_.extend({}, ControllerMixin, {
init: function() {
this._super.apply(this, arguments);
this.session = openerp.session;
},
}));
instance.web.Registry = instance.web.Class.extend({
/**
* Stores a mapping of arbitrary key (strings) to object paths (as strings
* as well).
*
* Resolves those paths at query time in order to always fetch the correct
* object, even if those objects have been overloaded/replaced after the
* registry was created.
*
* An object path is simply a dotted name from the instance root to the
* object pointed to (e.g. ``"instance.web.Session"`` for an OpenERP
* session object).
*
* @constructs instance.web.Registry
* @param {Object} mapping a mapping of keys to object-paths
*/
init: function (mapping) {
this.parent = null;
this.map = mapping || {};
},
/**
* Retrieves the object matching the provided key string.
*
* @param {String} key the key to fetch the object for
* @param {Boolean} [silent_error=false] returns undefined if the key or object is not found, rather than throwing an exception
* @returns {Class} the stored class, to initialize or null if not found
*/
get_object: function (key, silent_error) {
var path_string = this.map[key];
if (path_string === undefined) {
if (this.parent) {
return this.parent.get_object(key, silent_error);
}
if (silent_error) { return void 'nooo'; }
return null;
}
var object_match = instance;
var path = path_string.split('.');
// ignore first section
for(var i=1; i<path.length; ++i) {
object_match = object_match[path[i]];
if (object_match === undefined) {
if (silent_error) { return void 'noooooo'; }
return null;
}
}
return object_match;
},
/**
* Checks if the registry contains an object mapping for this key.
*
* @param {String} key key to look for
*/
contains: function (key) {
if (key === undefined) { return false; }
if (key in this.map) {
return true;
}
if (this.parent) {
return this.parent.contains(key);
}
return false;
},
/**
* Tries a number of keys, and returns the first object matching one of
* the keys.
*
* @param {Array} keys a sequence of keys to fetch the object for
* @returns {Class} the first class found matching an object
*/
get_any: function (keys) {
for (var i=0; i<keys.length; ++i) {
var key = keys[i];
if (!this.contains(key)) {
continue;
}
return this.get_object(key);
}
return null;
},
/**
* Adds a new key and value to the registry.
*
* This method can be chained.
*
* @param {String} key
* @param {String} object_path fully qualified dotted object path
* @returns {instance.web.Registry} itself
*/
add: function (key, object_path) {
this.map[key] = object_path;
return this;
},
/**
* Creates and returns a copy of the current mapping, with the provided
* mapping argument added in (replacing existing keys if needed)
*
* Parent and child remain linked, a new key in the parent (which is not
* overwritten by the child) will appear in the child.
*
* @param {Object} [mapping={}] a mapping of keys to object-paths
*/
extend: function (mapping) {
var child = new instance.web.Registry(mapping);
child.parent = this;
return child;
},
/**
* @deprecated use Registry#extend
*/
clone: function (mapping) {
console.warn('Registry#clone is deprecated, use Registry#extend');
return this.extend(mapping);
}
});
instance.web.JsonRPC = instance.web.Class.extend(instance.web.PropertiesMixin, {
triggers: {
'request': 'Request sent',
'response': 'Response received',
'response_failed': 'HTTP Error response or timeout received',
'error': 'The received response is an JSON-RPC error',
},
/**
* @constructs instance.web.JsonRPC
*
* @param {String} [server] JSON-RPC endpoint hostname
* @param {String} [port] JSON-RPC endpoint port
*/
init: function() {
instance.web.PropertiesMixin.init.call(this);
this.server = null;
this.debug = ($.deparam($.param.querystring()).debug !== undefined);
this.override_session = false;
this.session_id = undefined;
},
setup: function(origin) {
var window_origin = location.protocol+"//"+location.host, self=this;
this.origin = origin ? _.str.rtrim(origin,'/') : window_origin;
this.prefix = this.origin;
this.server = this.origin; // keep chs happy
this.rpc_function = (this.origin == window_origin) ? this.rpc_json : this.rpc_jsonp;
},
/**
* Executes an RPC call, registering the provided callbacks.
*
* Registers a default error callback if none is provided, and handles
* setting the correct session id and session context in the parameter
* objects
*
* @param {String} url RPC endpoint
* @param {Object} params call parameters
* @param {Object} options additional options for rpc call
* @param {Function} success_callback function to execute on RPC call success
* @param {Function} error_callback function to execute on RPC call failure
* @returns {jQuery.Deferred} jquery-provided ajax deferred
*/
rpc: function(url, params, options) {
var self = this;
options = options || {};
// url can be an $.ajax option object
if (_.isString(url)) {
url = { url: url };
}
_.defaults(params, {
context: this.user_context || {}
});
// Construct a JSON-RPC2 request, method is currently unused
var payload = {
jsonrpc: '2.0',
method: 'call',
params: params,
id: _.uniqueId('r')
};
var deferred = $.Deferred();
if (! options.shadow)
this.trigger('request', url, payload);
if (options.timeout)
url.timeout = options.timeout;
this.rpc_function(url, payload).then(
function (response, textStatus, jqXHR) {
if (! options.shadow)
self.trigger('response', response);
if (!response.error) {
deferred.resolve(response["result"], textStatus, jqXHR);
} else if (response.error.code === 100) {
self.uid = false;
} else {
deferred.reject(response.error, $.Event());
}
},
function(jqXHR, textStatus, errorThrown) {
if (! options.shadow)
self.trigger('response_failed', jqXHR);
var error = {
code: -32098,
message: "XmlHttpRequestError " + errorThrown,
data: {type: "xhr"+textStatus, debug: jqXHR.responseText, objects: [jqXHR, errorThrown] }
};
deferred.reject(error, $.Event());
});
// Allow deferred user to disable rpc_error call in fail
deferred.fail(function() {
deferred.fail(function(error, event) {
if (!event.isDefaultPrevented()) {
self.trigger('error', error, event);
}
});
});
return deferred;
},
/**
* Raw JSON-RPC call
*
* @returns {jQuery.Deferred} ajax-webd deferred object
*/
rpc_json: function(url, payload) {
var self = this;
var ajax = _.extend({
type: "POST",
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(payload),
processData: false,
headers: {
"X-Openerp-Session-Id": this.override_session ? this.session_id : undefined,
},
}, url);
if (this.synch)
ajax.async = false;
return $.ajax(ajax);
},
rpc_jsonp: function(url, payload) {
var self = this;
// extracted from payload to set on the url
var data = {
session_id: this.session_id,
id: payload.id,
};
var set_sid = function (response, textStatus, jqXHR) {
// If response give us the http session id, we store it for next requests...
if (response.session_id) {
self.session_id = response.session_id;
}
};
url.url = this.url(url.url, null);
var ajax = _.extend({
type: "GET",
dataType: 'jsonp',
jsonp: 'jsonp',
cache: false,
data: data
}, url);
if (this.synch)
ajax.async = false;
var payload_str = JSON.stringify(payload);
var payload_url = $.param({r:payload_str});
if (payload_url.length < 2000) {
// Direct jsonp request
ajax.data.r = payload_str;
return $.ajax(ajax).done(set_sid);
} else {
// Indirect jsonp request
var ifid = _.uniqueId('oe_rpc_iframe');
var display = self.debug ? 'block' : 'none';
var $iframe = $(_.str.sprintf("<iframe src='javascript:false;' name='%s' id='%s' style='display:%s'></iframe>", ifid, ifid, display));
var $form = $('<form>')
.attr('method', 'POST')
.attr('target', ifid)
.attr('enctype', "multipart/form-data")
.attr('action', ajax.url + '?jsonp=1&' + $.param(data))
.append($('<input type="hidden" name="r" />').attr('value', payload_str))
.hide()
.appendTo($('body'));
var cleanUp = function() {
if ($iframe) {
$iframe.unbind("load").remove();
}
$form.remove();
};
var deferred = $.Deferred();
// the first bind is fired up when the iframe is added to the DOM
$iframe.bind('load', function() {
// the second bind is fired up when the result of the form submission is received
$iframe.unbind('load').bind('load', function() {
$.ajax(ajax).always(function() {
cleanUp();
}).done(function() {
deferred.resolve.apply(deferred, arguments);
}).fail(function() {
deferred.reject.apply(deferred, arguments);
});
});
// now that the iframe can receive data, we fill and submit the form
$form.submit();
});
// append the iframe to the DOM (will trigger the first load)
$form.after($iframe);
return deferred.done(set_sid);
}
},
url: function(path, params) {
params = _.extend(params || {});
if (this.override_session)
params.session_id = this.session_id;
var qs = '?' + $.param(params);
var prefix = _.any(['http://', 'https://', '//'], _.bind(_.str.startsWith, null, path)) ? '' : this.prefix;
return prefix + path + qs;
},
});
instance.web.py_eval = function(expr, context) {
return py.eval(expr, _.extend({}, context || {}, {"true": true, "false": false, "null": null}));
};
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -1,5 +1,8 @@
openerp.web.data = function(instance) {
(function() {
var instance = openerp;
openerp.web.data = {};
/**
* Serializes the sort criterion array of a dataset into a form which can be
@ -58,19 +61,18 @@ instance.web.Query = instance.web.Class.extend({
},
_execute: function () {
var self = this;
return instance.session.rpc('/web/dataset/search_read', {
model: this._model.name,
fields: this._fields || false,
return this._model.call('search_read', {
domain: instance.web.pyeval.eval('domains',
[this._model.domain(this._filter)]),
context: instance.web.pyeval.eval('contexts',
[this._model.context(this._context)]),
fields: this._fields || false,
offset: this._offset,
limit: this._limit,
sort: instance.web.serialize_sort(this._order_by)
order: instance.web.serialize_sort(this._order_by),
context: instance.web.pyeval.eval('contexts',
[this._model.context(this._context)]),
}).then(function (results) {
self._count = results.length;
return results.records;
return results;
}, null);
},
/**
@ -253,20 +255,42 @@ instance.web.QueryGroup = instance.web.Class.extend({
}
});
instance.web.Model = instance.web.Class.extend({
instance.web.Model.include({
/**
* @constructs instance.web.Model
* @extends instance.web.Class
*
* @param {String} model_name name of the OpenERP model this object is bound to
* @param {Object} [context]
* @param {Array} [domain]
*/
init: function (model_name, context, domain) {
this.name = model_name;
new openerp.web.Model([session,] model_name[, context[, domain]])
@constructs instance.web.Model
@extends instance.web.Class
@param {openerp.web.Session} [session] The session object used to communicate with
the server.
@param {String} model_name name of the OpenERP model this object is bound to
@param {Object} [context]
@param {Array} [domain]
*/
init: function() {
var session, model_name, context, domain;
var args = _.toArray(arguments);
args.reverse();
session = args.pop();
if (session && ! (session instanceof openerp.web.Session)) {
model_name = session;
session = null;
} else {
model_name = args.pop();
}
context = args.length > 0 ? args.pop() : null;
domain = args.length > 0 ? args.pop() : null;
this._super(session, model_name, context, domain);
this._context = context || {};
this._domain = domain || [];
},
session: function() {
if (! this._session)
return instance.session;
return this._super();
},
/**
* @deprecated does not allow to specify kwargs, directly use call() instead
*/
@ -294,13 +318,7 @@ instance.web.Model = instance.web.Class.extend({
args = [];
}
instance.web.pyeval.ensure_evaluated(args, kwargs);
var debug = instance.session.debug ? '/'+this.name+':'+method : '';
return instance.session.rpc('/web/dataset/call_kw' + debug, {
model: this.name,
method: method,
args: args,
kwargs: kwargs
}, options);
return this._super(method, args, kwargs, options);
},
/**
* Fetches a Query instance bound to this model, for searching
@ -318,7 +336,7 @@ instance.web.Model = instance.web.Class.extend({
* @param {String} signal signal to trigger on the workflow
*/
exec_workflow: function (id, signal) {
return instance.session.rpc('/web/dataset/exec_workflow', {
return this.session().rpc('/web/dataset/exec_workflow', {
model: this.name,
id: id,
signal: signal
@ -355,7 +373,7 @@ instance.web.Model = instance.web.Class.extend({
*/
call_button: function (method, args) {
instance.web.pyeval.ensure_evaluated(args, {});
return instance.session.rpc('/web/dataset/call_button', {
return this.session().rpc('/web/dataset/call_button', {
model: this.name,
method: method,
// Should not be necessary anymore. Integrate remote in this?
@ -1071,6 +1089,6 @@ instance.web.DropMisordered = instance.web.Class.extend({
}
});
};
})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -1,4 +1,9 @@
openerp.web.data_export = function(instance) {
(function() {
var instance = openerp;
openerp.web.data_export = {};
var QWeb = instance.web.qweb,
_t = instance.web._t;
instance.web.DataExport = instance.web.Dialog.extend({
@ -408,4 +413,4 @@ instance.web.DataExport = instance.web.Dialog.extend({
}
});
};
})();

View File

@ -1,5 +1,9 @@
openerp.web.dates = function(instance) {
(function() {
var instance = openerp;
openerp.web.dates = {};
var _t = instance.web._t;
/**
@ -152,4 +156,4 @@ instance.web.time_to_str = function(obj) {
+ zpad(obj.getSeconds(),2);
};
};
})();

View File

@ -1,5 +1,9 @@
openerp.web.formats = function(instance) {
(function() {
var instance = openerp;
openerp.web.formats = {};
var _t = instance.web._t;
/**
@ -345,4 +349,4 @@ instance.web.round_decimals = function(value, decimals){
return instance.web.round_precision(value, Math.pow(10,-decimals));
};
};
})();

View File

@ -1,3 +1,34 @@
/*
* Copyright (c) 2012, OpenERP S.A.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
The only dependencies of this file are underscore >= 1.3.1, jQuery >= 1.8.3 and
QWeb >= 1.0.0 . No dependencies shall be added.
This file must compile in EcmaScript 3 and work in IE7.
*/
(function() {
/* jshint es3: true */
@ -5,7 +36,6 @@
function declare($, _, QWeb2) {
var openerp = {};
openerp.web = {};
/**
* Improved John Resig's inheritance, based on:
@ -21,7 +51,7 @@ openerp.web = {};
*
* Example:
*
* var Person = openerp.web.Class.extend({
* var Person = openerp.Class.extend({
* init: function(isDancing){
* this.dancing = isDancing;
* },
@ -57,14 +87,14 @@ openerp.web = {};
var initializing = false,
fnTest = /xyz/.test(function(){xyz();}) ? /\b_super\b/ : /.*/;
// The web Class implementation (does nothing)
openerp.web.Class = function(){};
openerp.Class = function(){};
/**
* Subclass an existing class
*
* @param {Object} prop class-level properties (class attributes and instance methods) to set on the new class
*/
openerp.web.Class.extend = function() {
openerp.Class.extend = function() {
var _super = this.prototype;
// Support mixins arguments
var args = _.toArray(arguments);
@ -104,7 +134,7 @@ openerp.web = {};
// The dummy class constructor
function Class() {
if(this.constructor !== openerp.web.Class){
if(this.constructor !== openerp.Class){
throw new Error("You can only instanciate objects with the 'new' operator");
}
// All construction is actually done in the init method
@ -165,7 +195,7 @@ openerp.web = {};
* When an object is destroyed, all its children are destroyed too releasing
* any resource they could have reserved before.
*/
openerp.web.ParentedMixin = {
openerp.ParentedMixin = {
__parentedMixin : true,
init: function() {
this.__parentedDestroyed = false;
@ -274,7 +304,7 @@ openerp.web.ParentedMixin = {
* http://backbonejs.org
*
*/
var Events = openerp.web.Class.extend({
var Events = openerp.Class.extend({
on : function(events, callback, context) {
var ev;
events = events.split(/\s+/);
@ -355,10 +385,10 @@ var Events = openerp.web.Class.extend({
}
});
openerp.web.EventDispatcherMixin = _.extend({}, openerp.web.ParentedMixin, {
openerp.EventDispatcherMixin = _.extend({}, openerp.ParentedMixin, {
__eventDispatcherMixin: true,
init: function() {
openerp.web.ParentedMixin.init.call(this);
openerp.ParentedMixin.init.call(this);
this.__edispatcherEvents = new Events();
this.__edispatcherRegisteredEvents = [];
},
@ -403,13 +433,13 @@ openerp.web.EventDispatcherMixin = _.extend({}, openerp.web.ParentedMixin, {
this.off(cal[0], cal[2], cal[1]);
}, this);
this.__edispatcherEvents.off();
openerp.web.ParentedMixin.destroy.call(this);
openerp.ParentedMixin.destroy.call(this);
}
});
openerp.web.PropertiesMixin = _.extend({}, openerp.web.EventDispatcherMixin, {
openerp.PropertiesMixin = _.extend({}, openerp.EventDispatcherMixin, {
init: function() {
openerp.web.EventDispatcherMixin.init.call(this);
openerp.EventDispatcherMixin.init.call(this);
this.__getterSetterInternalMap = {};
},
set: function(arg1, arg2, arg3) {
@ -493,7 +523,7 @@ openerp.web.PropertiesMixin = _.extend({}, openerp.web.EventDispatcherMixin, {
*
* That will kill the widget in a clean way and erase its content from the dom.
*/
openerp.web.Widget = openerp.web.Class.extend(openerp.web.PropertiesMixin, {
openerp.Widget = openerp.Class.extend(openerp.PropertiesMixin, {
// Backbone-ish API
tagName: 'div',
id: null,
@ -510,21 +540,21 @@ openerp.web.Widget = openerp.web.Class.extend(openerp.web.PropertiesMixin, {
/**
* Constructs the widget and sets its parent if a parent is given.
*
* @constructs openerp.web.Widget
* @constructs openerp.Widget
*
* @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance.
* @param {openerp.Widget} parent Binds the current instance to the given Widget instance.
* When that widget is destroyed by calling destroy(), the current instance will be
* destroyed too. Can be null.
*/
init: function(parent) {
openerp.web.PropertiesMixin.init.call(this);
openerp.PropertiesMixin.init.call(this);
this.setParent(parent);
// Bind on_/do_* methods to this
// We might remove this automatic binding in the future
for (var name in this) {
if(typeof(this[name]) == "function") {
if((/^on_|^do_/).test(name)) {
this[name] = this[name].bind(this);
this[name] = _.bind(this[name], this);
}
}
}
@ -541,7 +571,7 @@ openerp.web.Widget = openerp.web.Class.extend(openerp.web.PropertiesMixin, {
if(this.$el) {
this.$el.remove();
}
openerp.web.PropertiesMixin.destroy.call(this);
openerp.PropertiesMixin.destroy.call(this);
},
/**
* Renders the current widget and appends it to the given jQuery object or Widget.
@ -622,8 +652,7 @@ openerp.web.Widget = openerp.web.Class.extend(openerp.web.PropertiesMixin, {
renderElement: function() {
var $el;
if (this.template) {
$el = $(_.str.trim(openerp.web.qweb.render(
this.template, {widget: this})));
$el = $(openerp.qweb.render(this.template, {widget: this}).trim());
} else {
$el = this._make_descriptive();
}
@ -762,13 +791,459 @@ openerp.web.Widget = openerp.web.Class.extend(openerp.web.PropertiesMixin, {
}
});
openerp.web.qweb = new QWeb2.Engine();
openerp.web.qweb.default_dict = {
'_' : _,
'JSON': JSON
var genericJsonRpc = function(fct_name, params, fct) {
var data = {
jsonrpc: "2.0",
method: fct_name,
params: params,
id: Math.floor(Math.random() * 1000 * 1000 * 1000)
};
return fct(data).pipe(function(result) {
if (result.error !== undefined) {
console.error("Server application error", result.error);
return $.Deferred().reject("server", result.error);
} else {
return result.result;
}
}, function() {
console.error("JsonRPC communication error", _.toArray(arguments));
var def = $.Deferred();
return def.reject.apply(def, ["communication"].concat(_.toArray(arguments)));
});
};
openerp.jsonRpc = function(url, fct_name, params, settings) {
return genericJsonRpc(fct_name, params, function(data) {
return $.ajax(url, _.extend({}, settings, {
url: url,
dataType: 'json',
type: 'POST',
data: JSON.stringify(data),
contentType: 'application/json'
}));
});
};
openerp.jsonpRpc = function(url, fct_name, params, settings) {
settings = settings || {};
return genericJsonRpc(fct_name, params, function(data) {
var payload_str = JSON.stringify(data);
var payload_url = $.param({r:payload_str});
var force2step = settings.force2step || false;
delete settings.force2step;
var session_id = settings.session_id || null;
delete settings.session_id;
if (payload_url.length < 2000 && ! force2step) {
return $.ajax(url, _.extend({}, settings, {
url: url,
dataType: 'jsonp',
jsonp: 'jsonp',
type: 'GET',
cache: false,
data: {r: payload_str, session_id: session_id}
}));
} else {
var args = {session_id: session_id, id: data.id};
var ifid = _.uniqueId('oe_rpc_iframe');
var html = "<iframe src='javascript:false;' name='" + ifid + "' id='" + ifid + "' style='display:none'></iframe>";
var $iframe = $(html);
var nurl = 'jsonp=1&' + $.param(args);
nurl = url.indexOf("?") !== -1 ? url + "&" + nurl : url + "?" + nurl;
var $form = $('<form>')
.attr('method', 'POST')
.attr('target', ifid)
.attr('enctype', "multipart/form-data")
.attr('action', nurl)
.append($('<input type="hidden" name="r" />').attr('value', payload_str))
.hide()
.appendTo($('body'));
var cleanUp = function() {
if ($iframe) {
$iframe.unbind("load").remove();
}
$form.remove();
};
var deferred = $.Deferred();
// the first bind is fired up when the iframe is added to the DOM
$iframe.bind('load', function() {
// the second bind is fired up when the result of the form submission is received
$iframe.unbind('load').bind('load', function() {
$.ajax({
url: url,
dataType: 'jsonp',
jsonp: 'jsonp',
type: 'GET',
cache: false,
data: {session_id: session_id, id: data.id}
}).always(function() {
cleanUp();
}).done(function() {
deferred.resolve.apply(deferred, arguments);
}).fail(function() {
deferred.reject.apply(deferred, arguments);
});
});
// now that the iframe can receive data, we fill and submit the form
$form.submit();
});
// append the iframe to the DOM (will trigger the first load)
$form.after($iframe);
return deferred;
}
});
};
openerp.Session = openerp.Class.extend(openerp.PropertiesMixin, {
triggers: {
'request': 'Request sent',
'response': 'Response received',
'response_failed': 'HTTP Error response or timeout received',
'error': 'The received response is an JSON-RPC error'
},
/**
@constructs openerp.Session
@param parent The parent of the newly created object.
@param {String} origin Url of the OpenERP server to contact with this session object
or `null` if the server to contact is the origin server.
@param {Dict} options A dictionary that can contain the following options:
* "override_session": Default to false. If true, the current session object will
not try to re-use a previously created session id stored in a cookie.
* "session_id": Default to null. If specified, the specified session_id will be used
by this session object. Specifying this option automatically implies that the option
"override_session" is set to true.
*/
init: function(parent, origin, options) {
openerp.PropertiesMixin.init.call(this, parent);
options = options || {};
this.server = null;
this.session_id = options.session_id || null;
this.override_session = options.override_session || !!options.session_id || false;
this.avoid_recursion = false;
this.setup(origin);
},
setup: function(origin) {
// must be able to customize server
var window_origin = location.protocol + "//" + location.host;
origin = origin ? origin.replace( /\/+$/, '') : window_origin;
if (!_.isUndefined(this.origin) && this.origin !== origin)
throw new Error('Session already bound to ' + this.origin);
else
this.origin = origin;
this.prefix = this.origin;
this.server = this.origin; // keep chs happy
this.origin_server = this.origin === window_origin;
},
/**
* (re)loads the content of a session: db name, username, user id, session
* context and status of the support contract
*
* @returns {$.Deferred} deferred indicating the session is done reloading
*/
session_reload: function () {
var self = this;
return self.rpc("/web/session/get_session_info", {}).then(function(result) {
delete result.session_id;
_.extend(self, result);
});
},
/**
* The session is validated either by login or by restoration of a previous session
*/
session_authenticate: function(db, login, password) {
var self = this;
var params = {db: db, login: login, password: password};
return this.rpc("/web/session/authenticate", params).then(function(result) {
if (!result.uid) {
return $.Deferred().reject();
}
delete result.session_id;
_.extend(self, result);
});
},
check_session_id: function() {
var self = this;
if (this.avoid_recursion)
return $.when();
if (this.session_id)
return $.when(); // we already have the session id
if (this.override_session || ! this.origin_server) {
// If we don't use the origin server we consider we should always create a new session.
// Even if some browsers could support cookies when using jsonp that behavior is
// not consistent and the browser creators are tending to removing that feature.
this.avoid_recursion = true;
return this.rpc("/gen_session_id", {}).then(function(result) {
self.session_id = result;
}).always(function() {
self.avoid_recursion = false;
});
} else {
// normal use case, just use the cookie
self.session_id = openerp.get_cookie("session_id");
return $.when();
}
},
/**
* Executes an RPC call, registering the provided callbacks.
*
* Registers a default error callback if none is provided, and handles
* setting the correct session id and session context in the parameter
* objects
*
* @param {String} url RPC endpoint
* @param {Object} params call parameters
* @param {Object} options additional options for rpc call
* @param {Function} success_callback function to execute on RPC call success
* @param {Function} error_callback function to execute on RPC call failure
* @returns {jQuery.Deferred} jquery-provided ajax deferred
*/
rpc: function(url, params, options) {
var self = this;
options = _.clone(options || {});
var shadow = options.shadow || false;
delete options.shadow;
return self.check_session_id().then(function() {
// TODO: remove
if (! _.isString(url)) {
_.extend(options, url);
url = url.url;
}
// TODO correct handling of timeouts
if (! shadow)
self.trigger('request');
var fct;
if (self.origin_server) {
fct = openerp.jsonRpc;
if (self.override_session) {
options.headers = _.extend({}, options.headers, {
"X-Openerp-Session-Id": self.override_session ? self.session_id || '' : ''
});
}
} else {
fct = openerp.jsonpRpc;
url = self.url(url, null);
options.session_id = self.session_id || '';
}
var p = fct(url, "call", params, options);
p = p.then(function (result) {
if (! shadow)
self.trigger('response');
return result;
}, function(type, error, textStatus, errorThrown) {
if (type === "server") {
if (! shadow)
self.trigger('response');
if (error.code === 100) {
self.uid = false;
}
return $.Deferred().reject(error, $.Event());
} else {
if (! shadow)
self.trigger('response_failed');
var nerror = {
code: -32098,
message: "XmlHttpRequestError " + errorThrown,
data: {type: "xhr"+textStatus, debug: error.responseText, objects: [error, errorThrown] }
};
return $.Deferred().reject(nerror, $.Event());
}
});
return p.fail(function() { // Allow deferred user to disable rpc_error call in fail
p.fail(function(error, event) {
if (!event.isDefaultPrevented()) {
self.trigger('error', error, event);
}
});
});
});
},
url: function(path, params) {
params = _.extend(params || {});
if (this.override_session || (! this.origin_server))
params.session_id = this.session_id;
var qs = $.param(params);
if (qs.length > 0)
qs = "?" + qs;
var prefix = _.any(['http://', 'https://', '//'], function(el) {
return path.length >= el.length && path.slice(0, el.length) === el;
}) ? '' : this.prefix;
return prefix + path + qs;
},
model: function(model_name) {
return new openerp.Model(this, model_name);
}
});
openerp.Model = openerp.Class.extend({
/**
new openerp.Model([session,] model_name)
@constructs instance.Model
@extends instance.Class
@param {openerp.Session} [session] The session object used to communicate with
the server.
@param {String} model_name name of the OpenERP model this object is bound to
@param {Object} [context]
@param {Array} [domain]
*/
init: function () {
var session, model_name;
var args = _.toArray(arguments);
args.reverse();
session = args.pop();
if (session && ! (session instanceof openerp.Session)) {
model_name = session;
session = null;
} else {
model_name = args.pop();
}
this.name = model_name;
this._session = session;
},
session: function() {
if (! this._session)
throw new Error("Not session specified");
return this._session;
},
/**
* Call a method (over RPC) on the bound OpenERP model.
*
* @param {String} method name of the method to call
* @param {Array} [args] positional arguments
* @param {Object} [kwargs] keyword arguments
* @param {Object} [options] additional options for the rpc() method
* @returns {jQuery.Deferred<>} call result
*/
call: function (method, args, kwargs, options) {
args = args || [];
kwargs = kwargs || {};
if (!_.isArray(args)) {
// call(method, kwargs)
kwargs = args;
args = [];
}
return this.session().rpc('/web/dataset/call_kw', {
model: this.name,
method: method,
args: args,
kwargs: kwargs
}, options);
}
});
/** OpenERP Translations */
openerp.TranslationDataBase = openerp.Class.extend(/** @lends instance.TranslationDataBase# */{
/**
* @constructs instance.TranslationDataBase
* @extends instance.Class
*/
init: function() {
this.db = {};
this.parameters = {"direction": 'ltr',
"date_format": '%m/%d/%Y',
"time_format": '%H:%M:%S',
"grouping": [],
"decimal_point": ".",
"thousands_sep": ","};
},
set_bundle: function(translation_bundle) {
var self = this;
this.db = {};
var modules = _.keys(translation_bundle.modules);
modules.sort();
if (_.include(modules, "web")) {
modules = ["web"].concat(_.without(modules, "web"));
}
_.each(modules, function(name) {
self.add_module_translation(translation_bundle.modules[name]);
});
if (translation_bundle.lang_parameters) {
this.parameters = translation_bundle.lang_parameters;
}
},
add_module_translation: function(mod) {
var self = this;
_.each(mod.messages, function(message) {
self.db[message.id] = message.string;
});
},
build_translation_function: function() {
var self = this;
var fcnt = function(str) {
var tmp = self.get(str);
return tmp === undefined ? str : tmp;
};
fcnt.database = this;
return fcnt;
},
get: function(key) {
return this.db[key];
},
/**
Loads the translations from an OpenERP server.
@param {openerp.Session} session The session object to contact the server.
@param {Array} [modules] The list of modules to load the translation. If not specified,
it will default to all the modules installed in the current database.
@param {Object} [lang] lang The language. If not specified it will default to the language
of the current user.
@returns {jQuery.Deferred}
*/
load_translations: function(session, modules, lang) {
var self = this;
return session.rpc('/web/webclient/translations', {
"mods": modules || null,
"lang": lang || null
}).done(function(trans) {
self.set_bundle(trans);
});
}
});
openerp._t = new openerp.TranslationDataBase().build_translation_function();
openerp.get_cookie = function(c_name) {
if (document.cookie.length > 0) {
var c_start = document.cookie.indexOf(c_name + "=");
if (c_start != -1) {
c_start = c_start + c_name.length + 1;
var c_end = document.cookie.indexOf(";", c_start);
if (c_end == -1) {
c_end = document.cookie.length;
}
return unescape(document.cookie.substring(c_start, c_end));
}
}
return "";
};
openerp.qweb = new QWeb2.Engine();
openerp.qweb.default_dict = {
'_' : _,
'JSON': JSON,
'_t' : openerp._t
};
openerp.Mutex = openerp.Class.extend({
init: function() {
this.def = $.Deferred().resolve();
},
exec: function(action) {
var current = this.def;
var next = this.def = $.Deferred();
return current.then(function() {
return $.when(action()).always(function() {
next.resolve();
});
});
}
});
openerp.declare = declare;
return openerp;

View File

@ -1,7 +1,10 @@
/*
* py.js helpers and setup
*/
openerp.web.pyeval = function (instance) {
(function() {
var instance = openerp;
instance.web.pyeval = {};
var obj = function () {};
@ -864,4 +867,4 @@ openerp.web.pyeval = function (instance) {
}
}, 0); });
};
};
})();

View File

@ -1,4 +1,9 @@
openerp.web.search = function(instance) {
(function() {
var instance = openerp;
openerp.web.search = {};
var QWeb = instance.web.qweb,
_t = instance.web._t,
_lt = instance.web._lt;
@ -2179,6 +2184,6 @@ instance.web.search.custom_filters = new instance.web.Registry({
'id': 'instance.web.search.ExtendedSearchProposition.Id'
});
};
})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -1,4 +1,7 @@
(function() {
openerp.test_support = {
setup_session: function (session) {
var origin = location.protocol+"//"+location.host;
_.extend(session, {
@ -76,3 +79,5 @@ openerp.test_support = {
});
}
};
})();

View File

@ -3,13 +3,12 @@ openerp.testing = {};
(function (testing) {
var dependencies = {
pyeval: [],
corelib: ['pyeval'],
coresetup: ['corelib'],
data: ['corelib', 'coresetup'],
core: ['pyeval'],
data: ['core'],
dates: [],
formats: ['coresetup', 'dates'],
chrome: ['corelib', 'coresetup'],
views: ['corelib', 'coresetup', 'data', 'chrome'],
formats: ['core', 'dates'],
chrome: ['core'],
views: ['core', 'data', 'chrome'],
search: ['views', 'formats'],
list: ['views', 'data'],
form: ['data', 'views', 'list', 'formats'],
@ -53,31 +52,34 @@ openerp.testing = {};
testing.mockifyRPC = function (instance, responses) {
var session = instance.session;
session.responses = responses || {};
session.rpc_function = function (url, payload) {
session.rpc = function(url, rparams, options) {
if (_.isString(url)) {
url = {url: url};
}
var fn, params;
var needle = payload.params.model + ':' + payload.params.method;
var needle = rparams.model + ':' + rparams.method;
if (url.url === '/web/dataset/call_kw'
&& needle in this.responses) {
fn = this.responses[needle];
params = [
payload.params.args || [],
payload.params.kwargs || {}
rparams.args || [],
rparams.kwargs || {}
];
} else {
fn = this.responses[url.url];
params = [payload];
params = [{params: rparams}];
}
if (!fn) {
return $.Deferred().reject({}, 'failed',
_.str.sprintf("Url %s not found in mock responses, with arguments %s",
url.url, JSON.stringify(payload.params))
url.url, JSON.stringify(rparams))
).promise();
}
try {
return $.when(fn.apply(null, params)).then(function (result) {
// Wrap for RPC layer unwrapper thingy
return {result: result};
return result;
});
} catch (e) {
// not sure why this looks like that

View File

@ -1,4 +1,6 @@
openerp.web.form = function (instance) {
(function() {
var instance = openerp;
var _t = instance.web._t,
_lt = instance.web._lt;
var QWeb = instance.web.qweb;
@ -5664,6 +5666,6 @@ instance.web.form.tags = new instance.web.Registry({
instance.web.form.custom_widgets = new instance.web.Registry({
});
};
})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -1,4 +1,7 @@
openerp.web.list = function (instance) {
(function() {
var instance = openerp;
openerp.web.list = {};
var _t = instance.web._t,
_lt = instance.web._lt;
var QWeb = instance.web.qweb;
@ -2318,5 +2321,5 @@ instance.web.list.Many2Many = instance.web.list.Column.extend({
return this._super(row_data, options);
}
});
};
})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -2,7 +2,10 @@
* handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out
* @namespace
*/
openerp.web.list_editable = function (instance) {
(function() {
var instance = openerp;
openerp.web.list_editable = {};
var _t = instance.web._t;
// editability status of list rows
@ -834,4 +837,4 @@ openerp.web.list_editable = function (instance) {
return null;
}
});
};
})();

View File

@ -2,7 +2,10 @@
* OpenERP web library
*---------------------------------------------------------*/
openerp.web.view_tree = function(instance) {
(function() {
var instance = openerp;
openerp.web.view_tree = {};
var QWeb = instance.web.qweb,
_lt = instance.web._lt;
@ -265,4 +268,4 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
this.hidden = true;
}
});
};
})();

View File

@ -2,7 +2,10 @@
* OpenERP web library
*---------------------------------------------------------*/
openerp.web.views = function(instance) {
(function() {
var instance = openerp;
openerp.web.views = {};
var QWeb = instance.web.qweb,
_t = instance.web._t;
@ -1616,6 +1619,6 @@ instance.web.xml_to_str = function(node) {
*/
instance.web.views = new instance.web.Registry();
};
})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:

View File

@ -18,16 +18,16 @@ openerp.testing.section('data.model.group_by', {
"should have single grouping field");
return group_result;
});
mock('/web/dataset/search_read', function (args) {
deepEqual(args.params.domain, [['bar', '=', 3]],
mock('foo:search_read', function (args, kwargs) {
deepEqual(kwargs.domain, [['bar', '=', 3]],
"should have domain matching that of group_by result");
return {records: [
return [
{bar: 3, id: 1},
{bar: 3, id: 2},
{bar: 3, id: 4},
{bar: 3, id: 8},
{bar: 3, id: 16}
], length: 5};
];
});
return m.query().group_by('bar')

View File

@ -1,5 +1,5 @@
openerp.testing.section('eval.types', {
dependencies: ['web.coresetup'],
dependencies: ['web.core'],
setup: function (instance) {
instance.session.uid = 42;
}
@ -562,7 +562,7 @@ openerp.testing.section('eval.edc.nonliterals', {
});
});
openerp.testing.section('eval.contexts', {
dependencies: ['web.coresetup']
dependencies: ['web.core']
}, function (test) {
test('context_recursive', function (instance) {
var context_to_eval = [{
@ -773,7 +773,7 @@ openerp.testing.section('eval.contexts', {
});
});
openerp.testing.section('eval.domains', {
dependencies: ['web.coresetup', 'web.dates']
dependencies: ['web.core', 'web.dates']
}, function (test) {
test('current_date', function (instance) {
var current_date = instance.web.date_to_str(new Date());
@ -802,7 +802,7 @@ openerp.testing.section('eval.domains', {
});
});
openerp.testing.section('eval.groupbys', {
dependencies: ['web.coresetup']
dependencies: ['web.core']
}, function (test) {
test('groupbys_00', function (instance) {
var result = instance.web.pyeval.eval('groupbys', [

View File

@ -1,5 +1,5 @@
openerp.testing.section('server-formats', {
dependencies: ['web.coresetup', 'web.dates']
dependencies: ['web.core', 'web.dates']
}, function (test) {
test('Parse server datetime', function (instance) {
var date = instance.web.str_to_datetime("2009-05-04 12:34:23");

View File

@ -5,10 +5,10 @@ var ropenerp = window.openerp;
var openerp = ropenerp.declare($, _, QWeb2);
ropenerp.testing.section('class', {
dependencies: ['web.corelib']
dependencies: ['web.core']
}, function (test) {
test('Basic class creation', function () {
var C = openerp.web.Class.extend({
var C = openerp.Class.extend({
foo: function () {
return this.somevar;
}
@ -20,12 +20,12 @@ ropenerp.testing.section('class', {
strictEqual(i.foo(), 3);
});
test('Class initialization', function () {
var C1 = openerp.web.Class.extend({
var C1 = openerp.Class.extend({
init: function () {
this.foo = 3;
}
});
var C2 = openerp.web.Class.extend({
var C2 = openerp.Class.extend({
init: function (arg) {
this.foo = arg;
}
@ -38,7 +38,7 @@ ropenerp.testing.section('class', {
strictEqual(i2.foo, 42);
});
test('Inheritance', function () {
var C0 = openerp.web.Class.extend({
var C0 = openerp.Class.extend({
foo: function () {
return 1;
}
@ -59,7 +59,7 @@ ropenerp.testing.section('class', {
strictEqual(new C2().foo(), 3);
});
test('In-place extension', function () {
var C0 = openerp.web.Class.extend({
var C0 = openerp.Class.extend({
foo: function () {
return 3;
},
@ -85,7 +85,7 @@ ropenerp.testing.section('class', {
strictEqual(new C0().qux(), 5);
});
test('In-place extension and inheritance', function () {
var C0 = openerp.web.Class.extend({
var C0 = openerp.Class.extend({
foo: function () { return 1; },
bar: function () { return 1; }
});
@ -103,7 +103,7 @@ ropenerp.testing.section('class', {
strictEqual(new C1().bar(), 2);
});
test('In-place extensions alter existing instances', function () {
var C0 = openerp.web.Class.extend({
var C0 = openerp.Class.extend({
foo: function () { return 1; },
bar: function () { return 1; }
});
@ -119,7 +119,7 @@ ropenerp.testing.section('class', {
strictEqual(i.bar(), 3);
});
test('In-place extension of subclassed types', function () {
var C0 = openerp.web.Class.extend({
var C0 = openerp.Class.extend({
foo: function () { return 1; },
bar: function () { return 1; }
});
@ -142,7 +142,7 @@ ropenerp.testing.section('class', {
ropenerp.testing.section('Widget.proxy', {
}, function (test) {
test('(String)', function () {
var W = openerp.web.Widget.extend({
var W = openerp.Widget.extend({
exec: function () {
this.executed = true;
}
@ -153,7 +153,7 @@ ropenerp.testing.section('Widget.proxy', {
ok(w.executed, 'should execute the named method in the right context');
});
test('(String)(*args)', function () {
var W = openerp.web.Widget.extend({
var W = openerp.Widget.extend({
exec: function (arg) {
this.executed = arg;
}
@ -167,7 +167,7 @@ ropenerp.testing.section('Widget.proxy', {
test('(String), include', function () {
// the proxy function should handle methods being changed on the class
// and should always proxy "by name", to the most recent one
var W = openerp.web.Widget.extend({
var W = openerp.Widget.extend({
exec: function () {
this.executed = 1;
}
@ -183,14 +183,14 @@ ropenerp.testing.section('Widget.proxy', {
});
test('(Function)', function () {
var w = new (openerp.web.Widget.extend({ }))();
var w = new (openerp.Widget.extend({ }))();
var fn = w.proxy(function () { this.executed = true; });
fn();
ok(w.executed, "should set the function's context (like Function#bind)");
});
test('(Function)(*args)', function () {
var w = new (openerp.web.Widget.extend({ }))();
var w = new (openerp.Widget.extend({ }))();
var fn = w.proxy(function (arg) { this.executed = arg; });
fn(42);
@ -199,8 +199,8 @@ ropenerp.testing.section('Widget.proxy', {
});
ropenerp.testing.section('Widget.renderElement', {
setup: function () {
openerp.web.qweb = new QWeb2.Engine();
openerp.web.qweb.add_template(
openerp.qweb = new QWeb2.Engine();
openerp.qweb.add_template(
'<no>' +
'<t t-name="test.widget.template">' +
'<ol>' +
@ -218,7 +218,7 @@ ropenerp.testing.section('Widget.renderElement', {
}
}, function (test) {
test('no template, default', function () {
var w = new (openerp.web.Widget.extend({ }))();
var w = new (openerp.Widget.extend({ }))();
var $original = w.$el;
ok($original, "should initially have a root element");
@ -233,7 +233,7 @@ ropenerp.testing.section('Widget.renderElement', {
ok(_.isEmpty(w.$el.html(), "should not have generated any content"));
});
test('no template, custom tag', function () {
var w = new (openerp.web.Widget.extend({
var w = new (openerp.Widget.extend({
tagName: 'ul'
}))();
w.renderElement();
@ -241,7 +241,7 @@ ropenerp.testing.section('Widget.renderElement', {
equal(w.el.nodeName, 'UL', "should have generated the custom element tag");
});
test('no template, @id', function () {
var w = new (openerp.web.Widget.extend({
var w = new (openerp.Widget.extend({
id: 'foo'
}))();
w.renderElement();
@ -251,7 +251,7 @@ ropenerp.testing.section('Widget.renderElement', {
equal(w.el.id, 'foo', "should also be available via property");
});
test('no template, @className', function () {
var w = new (openerp.web.Widget.extend({
var w = new (openerp.Widget.extend({
className: 'oe_some_class'
}))();
w.renderElement();
@ -260,7 +260,7 @@ ropenerp.testing.section('Widget.renderElement', {
equal(w.$el.attr('class'), 'oe_some_class', "should have the right attribute");
});
test('no template, bunch of attributes', function () {
var w = new (openerp.web.Widget.extend({
var w = new (openerp.Widget.extend({
attributes: {
'id': 'some_id',
'class': 'some_class',
@ -287,7 +287,7 @@ ropenerp.testing.section('Widget.renderElement', {
});
test('template', function () {
var w = new (openerp.web.Widget.extend({
var w = new (openerp.Widget.extend({
template: 'test.widget.template'
}))();
w.renderElement();
@ -297,7 +297,7 @@ ropenerp.testing.section('Widget.renderElement', {
equal(w.el.textContent, '01234');
});
test('repeated', { asserts: 4 }, function (_unused, $fix) {
var w = new (openerp.web.Widget.extend({
var w = new (openerp.Widget.extend({
template: 'test.widget.template-value'
}))();
w.value = 42;
@ -314,8 +314,8 @@ ropenerp.testing.section('Widget.renderElement', {
});
ropenerp.testing.section('Widget.$', {
setup: function () {
openerp.web.qweb = new QWeb2.Engine();
openerp.web.qweb.add_template(
openerp.qweb = new QWeb2.Engine();
openerp.qweb.add_template(
'<no>' +
'<t t-name="test.widget.template">' +
'<ol>' +
@ -330,7 +330,7 @@ ropenerp.testing.section('Widget.$', {
}
}, function (test) {
test('basic-alias', function () {
var w = new (openerp.web.Widget.extend({
var w = new (openerp.Widget.extend({
template: 'test.widget.template'
}))();
w.renderElement();
@ -341,8 +341,8 @@ ropenerp.testing.section('Widget.$', {
});
ropenerp.testing.section('Widget.events', {
setup: function () {
openerp.web.qweb = new QWeb2.Engine();
openerp.web.qweb.add_template(
openerp.qweb = new QWeb2.Engine();
openerp.qweb.add_template(
'<no>' +
'<t t-name="test.widget.template">' +
'<ol>' +
@ -358,7 +358,7 @@ ropenerp.testing.section('Widget.events', {
}, function (test) {
test('delegate', function () {
var a = [];
var w = new (openerp.web.Widget.extend({
var w = new (openerp.Widget.extend({
template: 'test.widget.template',
events: {
'click': function () {
@ -382,7 +382,7 @@ ropenerp.testing.section('Widget.events', {
});
test('undelegate', function () {
var clicked = false, newclicked = false;
var w = new (openerp.web.Widget.extend({
var w = new (openerp.Widget.extend({
template: 'test.widget.template',
events: { 'click li': function () { clicked = true; } }
}))();

View File

@ -27,8 +27,7 @@
<script src="/web/static/lib/py.js/lib/py.js"></script>
<script src="/web/static/src/js/boot.js"></script>
<script src="/web/static/src/js/corelib.js"></script>
<script src="/web/static/src/js/coresetup.js"></script>
<script src="/web/static/src/js/core.js"></script>
<script src="/web/static/src/js/dates.js"></script>
<script src="/web/static/src/js/formats.js"></script>
<script src="/web/static/src/js/chrome.js"></script>

View File

@ -0,0 +1,181 @@
(function() {
var ropenerp = window.openerp;
var openerp = ropenerp.declare($, _, QWeb2);
ropenerp.testing.section('jsonrpc', {},
function (test) {
test('basic-jsonrpc', {asserts: 1}, function () {
var session = new openerp.Session();
return session.rpc("/gen_session_id", {}).then(function(result) {
ok(result.length > 0, "Result returned by /gen_session_id");
});
});
test('basic-jsonprpc', {asserts: 1}, function () {
var session = new openerp.Session();
session.origin_server = false;
return session.rpc("/gen_session_id", {}).then(function(result) {
ok(result.length > 0, "Result returned by /gen_session_id");
});
});
// desactivated because the phantomjs runner crash
/*test('basic-jsonprpc2', {asserts: 1}, function () {
var session = new openerp.Session();
session.origin_server = false;
return session.rpc("/gen_session_id", {}, {force2step: true}).then(function(result) {
ok(result.length > 0, "Result returned by /gen_session_id");
});
});*/
test('session-jsonrpc', {asserts: 2}, function () {
var session = new openerp.Session();
var tmp = _.uniqueId("something");
return session.rpc("/web/tests/set_session_value", {value: tmp}).then(function() {
ok(true, "set_session returned");
return session.rpc("/web/tests/get_session_value", {});
}).then(function(result) {
equal(result, tmp, "Got the same value from the session");
});
});
test('session-jsonprpc', {asserts: 2}, function () {
var session = new openerp.Session();
session.origin_server = false;
var tmp = _.uniqueId("something");
return session.rpc("/web/tests/set_session_value", {value: tmp}).then(function() {
ok(true, "set_session returned");
return session.rpc("/web/tests/get_session_value", {});
}).then(function(result) {
equal(result, tmp, "Got the same value from the session");
});
});
// desactivated because the phantomjs runner crash
/*test('session-jsonprpc2', {asserts: 2}, function () {
var session = new openerp.Session();
session.origin_server = false;
var tmp = _.uniqueId("something");
return session.rpc("/web/tests/set_session_value", {value: tmp}, {force2step: true}).then(function() {
ok(true, "set_session returned");
return session.rpc("/web/tests/get_session_value", {}, {force2step: true});
}).then(function(result) {
equal(result, tmp, "Got the same value from the session");
});
});*/
test('overridesession-jsonrpc', {asserts: 4}, function () {
var origin_session = new openerp.Session();
var origin_tmp = _.uniqueId("something");
var session = new openerp.Session(null, null, {override_session: true});
var tmp = _.uniqueId("something_else");
return session.rpc("/web/tests/set_session_value", {value: tmp}).then(function() {
ok(true, "set_session returned");
return origin_session.rpc("/web/tests/set_session_value", {value: origin_tmp});
}).then(function(result) {
ok(true, "set_session on origin returned");
return session.rpc("/web/tests/get_session_value", {});
}).then(function(result) {
equal(result, tmp, "Got the same value from the session");
notEqual(result, origin_tmp, "Values in the different sessions should be different");
});
});
test('overridesession-jsonprpc', {asserts: 4}, function () {
var origin_session = new openerp.Session();
var origin_tmp = _.uniqueId("something");
var session = new openerp.Session(null, null, {override_session: true});
var tmp = _.uniqueId("something_else");
session.origin_server = false;
return session.rpc("/web/tests/set_session_value", {value: tmp}).then(function() {
ok(true, "set_session returned");
return origin_session.rpc("/web/tests/set_session_value", {value: origin_tmp});
}).then(function(result) {
ok(true, "set_session on origin returned");
return session.rpc("/web/tests/get_session_value", {});
}).then(function(result) {
equal(result, tmp, "Got the same value from the session");
notEqual(result, origin_tmp, "Values in the different sessions should be different");
});
});
// desactivated because the phantomjs runner crash
/*test('overridesession-jsonprpc2', {asserts: 4}, function () {
var origin_session = new openerp.Session();
var origin_tmp = _.uniqueId("something");
var session = new openerp.Session(null, null, {override_session: true});
var tmp = _.uniqueId("something_else");
session.origin_server = false;
return session.rpc("/web/tests/set_session_value", {value: tmp}, {force2step: true}).then(function() {
ok(true, "set_session returned");
return origin_session.rpc("/web/tests/set_session_value", {value: origin_tmp});
}).then(function(result) {
ok(true, "set_session on origin returned");
return session.rpc("/web/tests/get_session_value", {}, {force2step: true});
}).then(function(result) {
equal(result, tmp, "Got the same value from the session");
notEqual(result, origin_tmp, "Values in the different sessions should be different");
});
});*/
});
// desactivated because I can't manage to make these work in the runbot
/*
var login = "admin";
var password = "admin";
var db = null;
ropenerp.testing.section('jsonrpc-auth', {
setup: function() {
var session = new openerp.Session();
return session.session_reload().then(function() {
db = session.db;
ok(db, "db must be valid");
});
},
},
function (test) {
test('basic-auth', {asserts: 4}, function () {
var session = new openerp.Session();
equal(session.uid, undefined, "uid is expected to be undefined");
return session.session_authenticate(db, login, password).then(function() {
equal(session.uid, 1, "Admin's uid must be 1");
return session.rpc("/web/dataset/call_kw", {
model: "res.users",
method: "read",
args: [1, ["login"]],
kwargs: {},
}).then(function(result) {
equal(result.login, "admin", "Admin's name must be 'admin'");
});
});
});
test('share-sessions', {asserts: 7}, function () {
var session = new openerp.Session();
var session2;
return session.session_authenticate(db, login, password).then(function() {
equal(session.uid, 1, "Admin's uid must be 1");
session2 = new openerp.Session(null, null, {session_id: session.session_id});
equal(session2.uid, undefined, "uid should be undefined");
equal(session2.override_session, true, "overwrite_session should be true");
return session2.session_reload();
}).then(function() {
equal(session2.uid, session.uid);
equal(session2.uid, 1);
return session2.rpc("/web/dataset/call_kw", {
model: "res.users",
method: "read",
args: [1, ["login"]],
kwargs: {},
}).then(function(result) {
equal(result.login, "admin", "Admin's name must be 'admin'");
});
});
});
test('models', {asserts: 3}, function () {
var session = new openerp.Session();
return session.session_authenticate(db, login, password).then(function() {
return session.model("res.users").call("search_read", {fields: ["login"], domain: [["id", "=", 1]]});
}).then(function(result) {
equal(result.length, 1, "Must have one result");
equal(result[0].login, "admin", "Must have admin's login");
});
});
});*/
})();

View File

@ -1,5 +1,5 @@
openerp.testing.section('mutex', {
dependencies: ['web.coresetup'],
dependencies: ['web.core'],
setup: function (instance) {
}
}, function (test) {

View File

@ -1,5 +1,5 @@
openerp.testing.section('registry', {
dependencies: ['web.corelib'],
dependencies: ['web.core'],
setup: function (instance) {
instance.web.Foo = {};
instance.web.Bar = {};