[MERGE] upstream
bzr revid: fme@openerp.com-20130807100643-0olinq3584kzc3we
This commit is contained in:
commit
11fb809e60
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"directory": "static/lib/"
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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):
|
||||
|
|
|
@ -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
|
|
@ -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
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
|
@ -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__" ?
|
||||
|
|
|
@ -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,'&').replace(/</g,'<').replace(/>/g,'>')
|
||||
.replace(/"/g, '"').replace(/'/g, "'");
|
||||
}),
|
||||
escapeHTML: function(str) {
|
||||
return (''+str).replace(/[&<>"']/g, function(match){ return '&' + reversedEscapeChars[match] + ';'; });
|
||||
},
|
||||
|
||||
unescapeHTML: sArgs(function(str) {
|
||||
return str.replace(/</g, '<').replace(/>/g, '>')
|
||||
.replace(/"/g, '"').replace(/'/g, "'").replace(/&/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));
|
|
@ -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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};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);
|
|
@ -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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}),unescapeHTML:d(function(a){return a.replace(/</g,"<").replace(/>/g,
|
||||
">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/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);
|
|
@ -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]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
|
@ -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:
|
|
@ -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:
|
||||
|
|
|
@ -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({
|
|||
}
|
||||
});
|
||||
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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); });
|
||||
};
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 = {
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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', [
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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; } }
|
||||
}))();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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");
|
||||
});
|
||||
});
|
||||
});*/
|
||||
|
||||
})();
|
|
@ -1,5 +1,5 @@
|
|||
openerp.testing.section('mutex', {
|
||||
dependencies: ['web.coresetup'],
|
||||
dependencies: ['web.core'],
|
||||
setup: function (instance) {
|
||||
}
|
||||
}, function (test) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
openerp.testing.section('registry', {
|
||||
dependencies: ['web.corelib'],
|
||||
dependencies: ['web.core'],
|
||||
setup: function (instance) {
|
||||
instance.web.Foo = {};
|
||||
instance.web.Bar = {};
|
||||
|
|
Loading…
Reference in New Issue