[IMP] im_livechat: ported the javascript to the new openerp framework
bzr revid: nicolas.vanhoren@openerp.com-20130819123836-8ngjlvab8202tk65
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
<script type="text/javascript" src="{{url}}/im_livechat/static/ext/static/js/require.js"></script>
|
||||
<script type="text/javascript" src="{{url}}/im_livechat/static/ext/static/lib/requirejs/require.js"></script>
|
||||
<script type="text/javascript" src='{{url}}/im_livechat/loader?p={{parameters | json | escape}}'></script>
|
||||
@@ -1,19 +1,39 @@
|
||||
|
||||
(function() {
|
||||
|
||||
var tmpQWeb2 = window.QWeb2;
|
||||
|
||||
require.config({
|
||||
context: "oelivesupport",
|
||||
baseUrl: {{url | json}} + "/im_livechat/static/ext/static/js",
|
||||
baseUrl: {{url | json}},
|
||||
paths: {
|
||||
jquery: "im_livechat/static/ext/static/lib/jquery/jquery",
|
||||
underscore: "im_livechat/static/ext/static/lib/underscore/underscore",
|
||||
qweb2: "im_livechat/static/ext/static/lib/qweb/qweb2",
|
||||
openerp: "web/static/src/js/openerpframework",
|
||||
"jquery.achtung": "im_livechat/static/ext/static/lib/jquery-achtung/src/ui.achtung",
|
||||
livesupport: "im_livechat/static/ext/static/js/livesupport",
|
||||
},
|
||||
shim: {
|
||||
underscore: {
|
||||
init: function() {
|
||||
return _.noConflict();
|
||||
},
|
||||
},
|
||||
qweb2: {
|
||||
init: function() {
|
||||
var QWeb2 = window.QWeb2;
|
||||
window.QWeb2 = tmpQWeb2;
|
||||
return QWeb2;
|
||||
},
|
||||
},
|
||||
"jquery.achtung": {
|
||||
deps: ['jquery'],
|
||||
},
|
||||
},
|
||||
})(["livesupport", "jquery"], function(livesupport, jQuery) {
|
||||
jQuery.noConflict();
|
||||
console.log("loaded live support");
|
||||
livesupport.main({{url | json}}, {{db | json}}, "anonymous", "anonymous", {{channel | json}}, {
|
||||
buttonText: {{buttonText | json}},
|
||||
inputPlaceholder: {{inputPlaceholder | json}},
|
||||
@@ -22,3 +42,5 @@ require.config({
|
||||
userName: {{userName | json}} || undefined,
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"directory": "static/lib/"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
module.exports = function(grunt) {
|
||||
|
||||
grunt.initConfig({
|
||||
jshint: {
|
||||
src: ['static/js/*.js'],
|
||||
options: {
|
||||
sub: true, //[] instead of .
|
||||
evil: true, //eval
|
||||
laxbreak: true, //unsafe line breaks
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
|
||||
grunt.registerTask('test', []);
|
||||
|
||||
grunt.registerTask('default', ['jshint']);
|
||||
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "im_livechat",
|
||||
"version": "0.0.0",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"jquery": "1.8.3",
|
||||
"underscore": "1.3.1",
|
||||
"qweb": "git@github.com:OpenERP/qweb.git#~1.0.0",
|
||||
"jquery-achtung": "git://github.com/joshvarner/jquery-achtung.git",
|
||||
"requirejs": "~2.1.8"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"grunt": "*",
|
||||
"grunt-contrib-jshint": "*"
|
||||
}
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
/**
|
||||
* achtung 0.3.0
|
||||
*
|
||||
* Growl-like notifications for jQuery
|
||||
*
|
||||
* Copyright (c) 2009 Josh Varner <josh@voxwerk.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/mit-license.php
|
||||
* @author Josh Varner <josh@voxwerk.com>
|
||||
*/
|
||||
|
||||
/*globals jQuery,clearTimeout,document,navigator,setTimeout
|
||||
*/
|
||||
(function($) {
|
||||
|
||||
/**
|
||||
* This is based on the jQuery UI $.widget code. I would have just made this
|
||||
* a $.widget but I didn't want the jQuery UI dependency.
|
||||
*/
|
||||
$.fn.achtung = function(options)
|
||||
{
|
||||
var isMethodCall = (typeof options === 'string'),
|
||||
args = Array.prototype.slice.call(arguments, 0),
|
||||
name = 'achtung';
|
||||
|
||||
// handle initialization and non-getter methods
|
||||
return this.each(function() {
|
||||
var instance = $.data(this, name);
|
||||
|
||||
// prevent calls to internal methods
|
||||
if (isMethodCall && options.substring(0, 1) === '_') {
|
||||
return this;
|
||||
}
|
||||
|
||||
// constructor
|
||||
(!instance && !isMethodCall &&
|
||||
$.data(this, name, new $.achtung(this))._init(args));
|
||||
|
||||
// method call
|
||||
(instance && isMethodCall && $.isFunction(instance[options]) &&
|
||||
instance[options].apply(instance, args.slice(1)));
|
||||
});
|
||||
};
|
||||
|
||||
$.achtung = function(element)
|
||||
{
|
||||
var args = Array.prototype.slice.call(arguments, 0), $el;
|
||||
|
||||
if (!element || !element.nodeType) {
|
||||
$el = $('<div />');
|
||||
return $el.achtung.apply($el, args);
|
||||
}
|
||||
|
||||
this.$container = $(element);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static members
|
||||
**/
|
||||
$.extend($.achtung, {
|
||||
version: '0.3.0',
|
||||
$overlay: false,
|
||||
defaults: {
|
||||
timeout: 10,
|
||||
disableClose: false,
|
||||
icon: false,
|
||||
className: '',
|
||||
animateClassSwitch: false,
|
||||
showEffects: {'opacity':'toggle','height':'toggle'},
|
||||
hideEffects: {'opacity':'toggle','height':'toggle'},
|
||||
showEffectDuration: 500,
|
||||
hideEffectDuration: 700
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Non-static members
|
||||
**/
|
||||
$.extend($.achtung.prototype, {
|
||||
$container: false,
|
||||
closeTimer: false,
|
||||
options: {},
|
||||
|
||||
_init: function(args)
|
||||
{
|
||||
var o, self = this;
|
||||
|
||||
args = $.isArray(args) ? args : [];
|
||||
|
||||
|
||||
args.unshift($.achtung.defaults);
|
||||
args.unshift({});
|
||||
|
||||
o = this.options = $.extend.apply($, args);
|
||||
|
||||
if (!$.achtung.$overlay) {
|
||||
$.achtung.$overlay = $('<div id="achtung-overlay"></div>').appendTo(document.body);
|
||||
}
|
||||
|
||||
if (!o.disableClose) {
|
||||
$('<span class="achtung-close-button ui-icon ui-icon-close" />')
|
||||
.click(function () { self.close(); })
|
||||
.hover(function () { $(this).addClass('achtung-close-button-hover'); },
|
||||
function () { $(this).removeClass('achtung-close-button-hover'); })
|
||||
.prependTo(this.$container);
|
||||
}
|
||||
|
||||
this.changeIcon(o.icon, true);
|
||||
|
||||
if (o.message) {
|
||||
this.$container.append($('<span class="achtung-message">' + o.message + '</span>'));
|
||||
}
|
||||
|
||||
(o.className && this.$container.addClass(o.className));
|
||||
(o.css && this.$container.css(o.css));
|
||||
|
||||
this.$container
|
||||
.addClass('achtung')
|
||||
.appendTo($.achtung.$overlay);
|
||||
|
||||
if (o.showEffects) {
|
||||
this.$container.toggle();
|
||||
} else {
|
||||
this.$container.show();
|
||||
}
|
||||
|
||||
if (o.timeout > 0) {
|
||||
this.timeout(o.timeout);
|
||||
}
|
||||
},
|
||||
|
||||
timeout: function(timeout)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
if (this.closeTimer) {
|
||||
clearTimeout(this.closeTimer);
|
||||
}
|
||||
|
||||
this.closeTimer = setTimeout(function() { self.close(); }, timeout * 1000);
|
||||
this.options.timeout = timeout;
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the CSS class associated with this message, using
|
||||
* a transition if available (not availble in Safari/Webkit).
|
||||
* If no transition is available, the switch is immediate.
|
||||
*
|
||||
* #LATER Check if this has been corrected in Webkit or jQuery UI
|
||||
* #TODO Make transition time configurable
|
||||
* @param newClass string Name of new class to associate
|
||||
*/
|
||||
changeClass: function(newClass)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
if (this.options.className === newClass) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$container.queue(function() {
|
||||
if (!self.options.animateClassSwitch ||
|
||||
/webkit/.test(navigator.userAgent.toLowerCase()) ||
|
||||
!$.isFunction($.fn.switchClass)) {
|
||||
self.$container.removeClass(self.options.className);
|
||||
self.$container.addClass(newClass);
|
||||
} else {
|
||||
self.$container.switchClass(self.options.className, newClass, 500);
|
||||
}
|
||||
|
||||
self.options.className = newClass;
|
||||
self.$container.dequeue();
|
||||
});
|
||||
},
|
||||
|
||||
changeIcon: function(newIcon, force)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
if ((force !== true || newIcon === false) && this.options.icon === newIcon) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (force || this.options.icon === false) {
|
||||
this.$container.prepend($('<span class="achtung-message-icon ui-icon ' + newIcon + '" />'));
|
||||
this.options.icon = newIcon;
|
||||
return;
|
||||
} else if (newIcon === false) {
|
||||
this.$container.find('.achtung-message-icon').remove();
|
||||
this.options.icon = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.$container.queue(function() {
|
||||
var $span = $('.achtung-message-icon', self.$container);
|
||||
|
||||
if (!self.options.animateClassSwitch ||
|
||||
/webkit/.test(navigator.userAgent.toLowerCase()) ||
|
||||
!$.isFunction($.fn.switchClass)) {
|
||||
$span.removeClass(self.options.icon);
|
||||
$span.addClass(newIcon);
|
||||
} else {
|
||||
$span.switchClass(self.options.icon, newIcon, 500);
|
||||
}
|
||||
|
||||
self.options.icon = newIcon;
|
||||
self.$container.dequeue();
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
changeMessage: function(newMessage)
|
||||
{
|
||||
this.$container.queue(function() {
|
||||
$('.achtung-message', $(this)).html(newMessage);
|
||||
$(this).dequeue();
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
update: function(options)
|
||||
{
|
||||
(options.className && this.changeClass(options.className));
|
||||
(options.css && this.$container.css(options.css));
|
||||
(typeof(options.icon) !== 'undefined' && this.changeIcon(options.icon));
|
||||
(options.message && this.changeMessage(options.message));
|
||||
(options.timeout && this.timeout(options.timeout));
|
||||
},
|
||||
|
||||
close: function()
|
||||
{
|
||||
var o = this.options, $container = this.$container;
|
||||
|
||||
if (o.hideEffects) {
|
||||
this.$container.animate(o.hideEffects, o.hideEffectDuration);
|
||||
} else {
|
||||
this.$container.hide();
|
||||
}
|
||||
|
||||
$container.queue(function() {
|
||||
$container.removeData('achtung');
|
||||
$container.remove();
|
||||
|
||||
if ($.achtung.$overlay && $.achtung.$overlay.is(':empty')) {
|
||||
$.achtung.$overlay.remove();
|
||||
$.achtung.$overlay = false;
|
||||
}
|
||||
|
||||
$container.dequeue();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
-9555
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,21 @@
|
||||
|
||||
define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
"jquery.achtung"], function(nova, _, oeclient, require, $) {
|
||||
/*
|
||||
This file must compile in EcmaScript 3 and work in IE7.
|
||||
*/
|
||||
|
||||
define(["openerp", "underscore", "require", "jquery",
|
||||
"jquery.achtung"], function(openerp, _, require, $) {
|
||||
/* jshint es3: true */
|
||||
"use strict";
|
||||
|
||||
var _t = openerp._t;
|
||||
|
||||
var livesupport = {};
|
||||
|
||||
var templateEngine = new nova.TemplateEngine();
|
||||
templateEngine.extendEnvironment({"toUrl": _.bind(require.toUrl, require)});
|
||||
_.extend(openerp.qweb.default_dict, {
|
||||
'toUrl': _.bind(require.toUrl, require)
|
||||
});
|
||||
|
||||
var connection;
|
||||
|
||||
var defaultInputPlaceholder;
|
||||
@@ -14,30 +25,28 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
var defs = [];
|
||||
options = options || {};
|
||||
_.defaults(options, {
|
||||
buttonText: "Chat with one of our collaborators",
|
||||
inputPlaceholder: "How may I help you?",
|
||||
buttonText: _t("Chat with one of our collaborators"),
|
||||
inputPlaceholder: _t("How may I help you?"),
|
||||
defaultMessage: null,
|
||||
auto: false,
|
||||
userName: "Anonymous",
|
||||
userName: _t("Anonymous")
|
||||
});
|
||||
defaultInputPlaceholder = options.inputPlaceholder;
|
||||
userName = options.userName;
|
||||
defs.push($.ajax({
|
||||
url: require.toUrl("./livesupport_templates.js"),
|
||||
jsonp: false,
|
||||
jsonpCallback: "oe_livesupport_templates_callback",
|
||||
dataType: "jsonp",
|
||||
cache: true,
|
||||
}).then(function(content) {
|
||||
return templateEngine.loadFileContent(content);
|
||||
}));
|
||||
defs.push(add_css("../css/livesupport.css"));
|
||||
defs.push(add_css("./jquery.achtung.css"));
|
||||
// TODO : load QwebTemplates
|
||||
defs.push(add_css("im_livechat/static/ext/static/css/livesupport.css"));
|
||||
defs.push(add_css("im_livechat/static/ext/static/lib/jquery-achtung/src/ui.achtung.css"));
|
||||
|
||||
$.when.apply($, defs).then(function() {
|
||||
return $.when.apply($, defs).then(function() {
|
||||
console.log("starting live support customer app");
|
||||
connection = new oeclient.Connection(new oeclient.JsonpRPCConnector(server_url), db, login, password);
|
||||
connection.connector.call("/im_livechat/available", {db: db, channel: channel}).then(function(activated) {
|
||||
connection = new openerp.Session(null, server_url, { override_session: true });
|
||||
return connection.session_authenticate(db, login, password);
|
||||
}).then(function() {
|
||||
return connection.rpc('/web/proxy/load', {path: '/im_livechat/static/ext/static/js/livesupport_templates.xml'}).then(function(xml) {
|
||||
openerp.qweb.add_template(xml);
|
||||
});
|
||||
}).then(function() {
|
||||
return connection.rpc("/im_livechat/available", {db: db, channel: channel}).then(function(activated) {
|
||||
if (! activated & ! options.auto)
|
||||
return;
|
||||
var button = new livesupport.ChatButton(null, channel, options);
|
||||
@@ -63,19 +72,19 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
|
||||
var ERROR_DELAY = 5000;
|
||||
|
||||
livesupport.ChatButton = nova.Widget.$extend({
|
||||
livesupport.ChatButton = openerp.Widget.extend({
|
||||
className: "openerp_style oe_chat_button",
|
||||
events: {
|
||||
"click": "click",
|
||||
"click": "click"
|
||||
},
|
||||
__init__: function(parent, channel, options) {
|
||||
this.$super(parent);
|
||||
init: function(parent, channel, options) {
|
||||
this._super(parent);
|
||||
this.channel = channel;
|
||||
this.options = options;
|
||||
this.text = options.buttonText;
|
||||
},
|
||||
render: function() {
|
||||
this.$().append(templateEngine.chatButton({widget: this}));
|
||||
start: function() {
|
||||
this.$().append(openerp.qweb.render("chatButton", {widget: this}));
|
||||
},
|
||||
click: function() {
|
||||
if (! this.manager) {
|
||||
@@ -92,34 +101,33 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
def.reject();
|
||||
}, 5000);
|
||||
def.then(_.bind(this.chat, this), function() {
|
||||
notification("It seems the connection to the server is encountering problems, please try again later.");
|
||||
notification(_t("It seems the connection to the server is encountering problems, please try again later."));
|
||||
});
|
||||
},
|
||||
chat: function() {
|
||||
var self = this;
|
||||
if (this.manager.conversations.length > 0)
|
||||
return;
|
||||
connection.getModel("im_livechat.channel").call("get_available_user", [this.channel]).then(function(user_id) {
|
||||
connection.model("im_livechat.channel").call("get_available_user", [this.channel]).then(function(user_id) {
|
||||
if (! user_id) {
|
||||
notification("None of our collaborators seems to be available, please try again later.");
|
||||
notification(_t("None of our collaborators seems to be available, please try again later."));
|
||||
return;
|
||||
}
|
||||
self.manager.ensure_users([user_id]).then(function() {
|
||||
var conv = self.manager.activate_user(self.manager.get_user(user_id), true);
|
||||
if (self.options.defaultMessage) {
|
||||
conv.received_message({message: self.options.defaultMessage,
|
||||
date: oeclient.datetime_to_str(new Date())});
|
||||
date: openerp.datetime_to_str(new Date())});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
livesupport.ImUser = nova.Class.$extend({
|
||||
__include__: [nova.DynamicProperties],
|
||||
__init__: function(parent, user_rec) {
|
||||
nova.DynamicProperties.__init__.call(this, parent);
|
||||
user_rec.image_url = require.toUrl("../img/avatar/avatar.jpeg");
|
||||
livesupport.ImUser = openerp.Class.extend(openerp.PropertiesMixin, {
|
||||
init: function(parent, user_rec) {
|
||||
openerp.PropertiesMixin.init.call(this, parent);
|
||||
user_rec.image_url = require.toUrl("im_livechat/static/ext/static/img/avatar/avatar.jpeg");
|
||||
if (user_rec.image)
|
||||
user_rec.image_url = "data:image/png;base64," + user_rec.image;
|
||||
this.set(user_rec);
|
||||
@@ -131,20 +139,19 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
},
|
||||
destroy: function() {
|
||||
this.trigger("destroyed");
|
||||
nova.DynamicProperties.destroy.call(this);
|
||||
openerp.PropertiesMixin.destroy.call(this);
|
||||
},
|
||||
add_watcher: function() {
|
||||
this.set("watcher_count", this.get("watcher_count") + 1);
|
||||
},
|
||||
remove_watcher: function() {
|
||||
this.set("watcher_count", this.get("watcher_count") - 1);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
livesupport.ConversationManager = nova.Class.$extend({
|
||||
__include__: [nova.DynamicProperties],
|
||||
__init__: function(parent) {
|
||||
nova.DynamicProperties.__init__.call(this, parent);
|
||||
livesupport.ConversationManager = openerp.Class.extend(openerp.PropertiesMixin, {
|
||||
init: function(parent) {
|
||||
openerp.PropertiesMixin.init.call(this, parent);
|
||||
this.set("right_offset", 0);
|
||||
this.conversations = [];
|
||||
this.users = {};
|
||||
@@ -176,22 +183,22 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
var def = $.when(uuid);
|
||||
|
||||
if (! uuid) {
|
||||
def = connection.connector.call("/longpolling/im/gen_uuid", {});
|
||||
def = connection.rpc("/longpolling/im/gen_uuid", {});
|
||||
}
|
||||
return def.then(function(uuid) {
|
||||
localStorage["oe_livesupport_uuid"] = uuid;
|
||||
return connection.getModel("im.user").call("get_by_user_id", [uuid]);
|
||||
return connection.model("im.user").call("get_by_user_id", [uuid]);
|
||||
}).then(function(my_id) {
|
||||
self.my_id = my_id["id"];
|
||||
return connection.getModel("im.user").call("assign_name", [uuid, userName]);
|
||||
return connection.model("im.user").call("assign_name", [uuid, userName]);
|
||||
}).then(function() {
|
||||
return self.ensure_users([self.my_id])
|
||||
return self.ensure_users([self.my_id]);
|
||||
}).then(function() {
|
||||
var me = self.users_cache[self.my_id];
|
||||
delete self.users_cache[self.my_id];
|
||||
self.me = me;
|
||||
me.set("name", "You");
|
||||
return connection.connector.call("/longpolling/im/activated", {});
|
||||
return connection.rpc("/longpolling/im/activated", {});
|
||||
}).then(function(activated) {
|
||||
if (activated) {
|
||||
self.activated = true;
|
||||
@@ -203,7 +210,7 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
});
|
||||
},
|
||||
unload: function() {
|
||||
connection.getModel("im.user").call("im_disconnect", [], {uuid: this.me.get("uuid"), context: {}});
|
||||
connection.model("im.user").call("im_disconnect", [], {uuid: this.me.get("uuid"), context: {}});
|
||||
},
|
||||
ensure_users: function(user_ids) {
|
||||
var no_cache = {};
|
||||
@@ -215,7 +222,7 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
if (_.size(no_cache) === 0)
|
||||
return $.when();
|
||||
else
|
||||
return connection.getModel("im.user").call("read", [_.values(no_cache), []]).then(function(users) {
|
||||
return connection.model("im.user").call("read", [_.values(no_cache), []]).then(function(users) {
|
||||
self.add_to_user_cache(users);
|
||||
});
|
||||
},
|
||||
@@ -239,13 +246,13 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
var user_ids = _.map(this.users_cache, function(el) {
|
||||
return el.get("id");
|
||||
});
|
||||
connection.connector.call("/longpolling/im/poll", {
|
||||
connection.rpc("/longpolling/im/poll", {
|
||||
last: this.last,
|
||||
users_watch: user_ids,
|
||||
db: connection.database,
|
||||
uid: connection.userId,
|
||||
password: connection.password,
|
||||
uuid: self.me.get("uuid"),
|
||||
uuid: self.me.get("uuid")
|
||||
}).then(function(result) {
|
||||
_.each(result.users_status, function(el) {
|
||||
if (self.get_user(el.id))
|
||||
@@ -273,8 +280,8 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
return;
|
||||
}
|
||||
this.ting = new Audio(new Audio().canPlayType("audio/ogg; codecs=vorbis") ?
|
||||
require.toUrl("../audio/Ting.ogg") :
|
||||
require.toUrl("../audio/Ting.mp3")
|
||||
require.toUrl("im_livechat/static/ext/static/audio/Ting.ogg") :
|
||||
require.toUrl("im_livechat/static/ext/static/audio/Ting.mp3")
|
||||
);
|
||||
},
|
||||
window_focus_change: function() {
|
||||
@@ -283,10 +290,10 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
}
|
||||
},
|
||||
messages_change: function() {
|
||||
/*if (! instance.webclient.set_title_part)
|
||||
return;
|
||||
instance.webclient.set_title_part("im_messages", this.get("waiting_messages") === 0 ? undefined :
|
||||
_.str.sprintf(_t("%d Messages"), this.get("waiting_messages")));*/
|
||||
//if (! instance.webclient.set_title_part)
|
||||
// return;
|
||||
//instance.webclient.set_title_part("im_messages", this.get("waiting_messages") === 0 ? undefined :
|
||||
// _.str.sprintf(_t("%d Messages"), this.get("waiting_messages")));
|
||||
},
|
||||
activate_user: function(user, focus) {
|
||||
var conv = this.users[user.get('id')];
|
||||
@@ -326,19 +333,19 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
$(window).off("unload", this.unload_event_handler);
|
||||
$(window).unbind("blur", this.blur_hdl);
|
||||
$(window).unbind("focus", this.focus_hdl);
|
||||
nova.DynamicProperties.destroy.call(this);
|
||||
},
|
||||
openerp.PropertiesMixin.destroy.call(this);
|
||||
}
|
||||
});
|
||||
|
||||
livesupport.Conversation = nova.Widget.$extend({
|
||||
livesupport.Conversation = openerp.Widget.extend({
|
||||
className: "openerp_style oe_im_chatview",
|
||||
events: {
|
||||
"keydown input": "send_message",
|
||||
"click .oe_im_chatview_close": "destroy",
|
||||
"click .oe_im_chatview_header": "show_hide",
|
||||
"click .oe_im_chatview_header": "show_hide"
|
||||
},
|
||||
__init__: function(parent, user, me) {
|
||||
this.$super(parent);
|
||||
init: function(parent, user, me) {
|
||||
this._super(parent);
|
||||
this.me = me;
|
||||
this.user = user;
|
||||
this.user.add_watcher();
|
||||
@@ -347,8 +354,8 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
this.set("pending", 0);
|
||||
this.inputPlaceholder = defaultInputPlaceholder;
|
||||
},
|
||||
render: function() {
|
||||
this.$().append(templateEngine.conversation({widget: this}));
|
||||
start: function() {
|
||||
this.$().append(openerp.qweb.render("conversation", {widget: this}));
|
||||
var change_status = function() {
|
||||
this.$().toggleClass("oe_im_chatview_disconnected_status", this.user.get("im_status") === false);
|
||||
this.$(".oe_im_chatview_online").toggle(this.user.get("im_status") === true);
|
||||
@@ -371,11 +378,11 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
show_hide: function() {
|
||||
if (this.shown) {
|
||||
this.$().animate({
|
||||
height: this.$(".oe_im_chatview_header").outerHeight(),
|
||||
height: this.$(".oe_im_chatview_header").outerHeight()
|
||||
});
|
||||
} else {
|
||||
this.$().animate({
|
||||
height: this.full_height,
|
||||
height: this.full_height
|
||||
});
|
||||
}
|
||||
this.shown = ! this.shown;
|
||||
@@ -392,7 +399,7 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
} else {
|
||||
this.set("pending", this.get("pending") + 1);
|
||||
}
|
||||
this._add_bubble(this.user, message.message, oeclient.str_to_datetime(message.date));
|
||||
this._add_bubble(this.user, message.message, openerp.str_to_datetime(message.date));
|
||||
},
|
||||
send_message: function(e) {
|
||||
if(e && e.which !== 13) {
|
||||
@@ -404,7 +411,7 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
}
|
||||
this.$("input").val("");
|
||||
var send_it = _.bind(function() {
|
||||
var model = connection.getModel("im.message");
|
||||
var model = connection.model("im.message");
|
||||
return model.call("post", [mes, this.user.get('id')], {uuid: this.me.get("uuid"), context: {}});
|
||||
}, this);
|
||||
var tries = 0;
|
||||
@@ -430,7 +437,7 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
};
|
||||
date = "" + zpad(date.getHours(), 2) + ":" + zpad(date.getMinutes(), 2);
|
||||
|
||||
this.last_bubble = $(templateEngine.conversation_bubble({"items": items, "user": user, "time": date}));
|
||||
this.last_bubble = $(openerp.qweb.render("conversation_bubble", {"items": items, "user": user, "time": date}));
|
||||
$(this.$(".oe_im_chatview_content").children()[0]).append(this.last_bubble);
|
||||
this._go_bottom();
|
||||
},
|
||||
@@ -443,8 +450,8 @@ define(["nova", "underscore", "oeclient", "require", "jquery",
|
||||
destroy: function() {
|
||||
this.user.remove_watcher();
|
||||
this.trigger("destroyed");
|
||||
return this.$super();
|
||||
},
|
||||
return this._super();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
<%def name="conversation">
|
||||
<div class="oe_im_chatview_header">
|
||||
<img src="${toUrl('../img/green.png')}" class="oe_im_chatview_online"/>
|
||||
${widget.user.get('name') || 'You'}
|
||||
<button class="oe_im_chatview_close">×</button>
|
||||
</div>
|
||||
<div class="oe_im_chatview_disconnected">
|
||||
${widget.user.get("name") + " is offline. He/She will receive your messages on his/her next connection."}
|
||||
</div>
|
||||
<div class="oe_im_chatview_content">
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="oe_im_chatview_footer">
|
||||
<input class="oe_im_chatview_input" placeholder="${widget.inputPlaceholder}" />
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="conversation_bubble">
|
||||
<div class="oe_im_chatview_bubble">
|
||||
<div class="oe_im_chatview_clip">
|
||||
<img class="oe_im_chatview_avatar" src="${user.get('image_url')}"/>
|
||||
</div>
|
||||
<div class="oe_im_chatview_from">${user.get('name') || 'You'}</div>
|
||||
<div class="oe_im_chatview_bubble_list">
|
||||
% _.each(items, function(item) {
|
||||
<div class="oe_im_chatview_bubble_item">${item}</div>
|
||||
% });
|
||||
</div>
|
||||
<div class="oe_im_chatview_time">${time}</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="chatButton">
|
||||
${widget.text}
|
||||
</%def>
|
||||
@@ -1 +0,0 @@
|
||||
window.oe_livesupport_templates_callback("\n<%def name=\"conversation\">\n <div class=\"oe_im_chatview_header\">\n <img src=\"${toUrl('../img/green.png')}\" class=\"oe_im_chatview_online\"/>\n ${widget.user.get('name') || 'You'}\n <button class=\"oe_im_chatview_close\">\u00d7</button>\n </div>\n <div class=\"oe_im_chatview_disconnected\">\n ${widget.user.get(\"name\") + \" is offline. He/She will receive your messages on his/her next connection.\"}\n </div>\n <div class=\"oe_im_chatview_content\">\n <div></div>\n </div>\n <div class=\"oe_im_chatview_footer\">\n <input class=\"oe_im_chatview_input\" placeholder=\"${widget.inputPlaceholder}\" />\n </div>\n</%def>\n\n<%def name=\"conversation_bubble\">\n <div class=\"oe_im_chatview_bubble\">\n <div class=\"oe_im_chatview_clip\">\n <img class=\"oe_im_chatview_avatar\" src=\"${user.get('image_url')}\"/>\n </div>\n <div class=\"oe_im_chatview_from\">${user.get('name') || 'You'}</div>\n <div class=\"oe_im_chatview_bubble_list\">\n % _.each(items, function(item) {\n <div class=\"oe_im_chatview_bubble_item\">${item}</div>\n % });\n </div>\n <div class=\"oe_im_chatview_time\">${time}</div>\n </div>\n</%def>\n\n<%def name=\"chatButton\">\n ${widget.text}\n</%def>");
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<templates>
|
||||
<t t-name="conversation">
|
||||
<div class="oe_im_chatview_header">
|
||||
<img t-att-src="toUrl('im_livechat/static/ext/static/img/green.png')" class="oe_im_chatview_online"/>
|
||||
<t t-esc="widget.user.get('name') || _t('You')"/>
|
||||
<button class="oe_im_chatview_close">×</button>
|
||||
</div>
|
||||
<div class="oe_im_chatview_disconnected">
|
||||
<t t-esc='widget.user.get("name") + _t(" is offline. He/She will receive your messages on his/her next connection.")'/>
|
||||
</div>
|
||||
<div class="oe_im_chatview_content">
|
||||
<div></div>
|
||||
</div>
|
||||
<div class="oe_im_chatview_footer">
|
||||
<input class="oe_im_chatview_input" t-att-placeholder="widget.inputPlaceholder" />
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="conversation_bubble">
|
||||
<div class="oe_im_chatview_bubble">
|
||||
<div class="oe_im_chatview_clip">
|
||||
<img class="oe_im_chatview_avatar" t-att-src="user.get('image_url')"/>
|
||||
</div>
|
||||
<div class="oe_im_chatview_from"><t t-esc="user.get('name') || _t('You')"/></div>
|
||||
<div class="oe_im_chatview_bubble_list">
|
||||
<t t-foreach="items" t-as="item">
|
||||
<div class="oe_im_chatview_bubble_item"><t t-esc="item"/></div>
|
||||
</t>
|
||||
</div>
|
||||
<div class="oe_im_chatview_time"><t t-esc="time"/></div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="chatButton">
|
||||
<t t-esc="widget.text"/>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -1,988 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2012, Nicolas Vanhoren
|
||||
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.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
if (typeof(define) !== "undefined") { // requirejs
|
||||
define(["jquery", "underscore"], nova_declare);
|
||||
} else if (typeof(exports) !== "undefined") { // node
|
||||
var _ = require("underscore")
|
||||
_.extend(exports, nova_declare(null, _));
|
||||
} else { // define global variable 'nova'
|
||||
nova = nova_declare($, _);
|
||||
}
|
||||
|
||||
function nova_declare($, _) {
|
||||
var nova = {};
|
||||
nova.internal = {};
|
||||
|
||||
/*
|
||||
* Modified Armin Ronacher's Classy library.
|
||||
*
|
||||
* Defines The Class object. That object can be used to define and inherit classes using
|
||||
* the $extend() method.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* var Person = nova.Class.$extend({
|
||||
* __init__: function(isDancing){
|
||||
* this.dancing = isDancing;
|
||||
* },
|
||||
* dance: function(){
|
||||
* return this.dancing;
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* The __init__() method act as a constructor. This class can be instancied this way:
|
||||
*
|
||||
* var person = new Person(true);
|
||||
* person.dance();
|
||||
*
|
||||
* The Person class can also be extended again:
|
||||
*
|
||||
* var Ninja = Person.$extend({
|
||||
* __init__: function(){
|
||||
* this.$super( false );
|
||||
* },
|
||||
* dance: function(){
|
||||
* // Call the inherited version of dance()
|
||||
* return this.$super();
|
||||
* },
|
||||
* swingSword: function(){
|
||||
* return true;
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* When extending a class, each re-defined method can use this.$super() to call the previous
|
||||
* implementation of that method.
|
||||
*/
|
||||
/**
|
||||
* Classy - classy classes for JavaScript
|
||||
*
|
||||
* :copyright: (c) 2011 by Armin Ronacher.
|
||||
* :license: BSD.
|
||||
*/
|
||||
(function(){
|
||||
var
|
||||
context = this,
|
||||
disable_constructor = false;
|
||||
|
||||
/* we check if $super is in use by a class if we can. But first we have to
|
||||
check if the JavaScript interpreter supports that. This also matches
|
||||
to false positives later, but that does not do any harm besides slightly
|
||||
slowing calls down. */
|
||||
var probe_super = (function(){this.$super();}).toString().indexOf('$super') > 0;
|
||||
function usesSuper(obj) {
|
||||
return !probe_super || /\B\$super\b/.test(obj.toString());
|
||||
}
|
||||
|
||||
/* helper function to set the attribute of something to a value or
|
||||
removes it if the value is undefined. */
|
||||
function setOrUnset(obj, key, value) {
|
||||
if (value === undefined)
|
||||
delete obj[key];
|
||||
else
|
||||
obj[key] = value;
|
||||
}
|
||||
|
||||
/* gets the own property of an object */
|
||||
function getOwnProperty(obj, name) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, name)
|
||||
? obj[name] : undefined;
|
||||
}
|
||||
|
||||
/* instanciate a class without calling the constructor */
|
||||
function cheapNew(cls) {
|
||||
disable_constructor = true;
|
||||
var rv = new cls;
|
||||
disable_constructor = false;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* the base class we export */
|
||||
var Class = function() {};
|
||||
|
||||
/* extend functionality */
|
||||
Class.$extend = function(properties) {
|
||||
var super_prototype = this.prototype;
|
||||
|
||||
/* disable constructors and instanciate prototype. Because the
|
||||
prototype can't raise an exception when created, we are safe
|
||||
without a try/finally here. */
|
||||
var prototype = cheapNew(this);
|
||||
|
||||
/* copy all properties of the includes over if there are any */
|
||||
prototype.__mixin_ids = _.clone(prototype.__mixin_ids || {});
|
||||
if (properties.__include__)
|
||||
for (var i = 0, n = properties.__include__.length; i != n; ++i) {
|
||||
var mixin = properties.__include__[i];
|
||||
if (mixin instanceof nova.Mixin) {
|
||||
_.extend(prototype.__mixin_ids, mixin.__mixin_ids);
|
||||
mixin = mixin.__mixin_properties;
|
||||
}
|
||||
for (var name in mixin) {
|
||||
var value = getOwnProperty(mixin, name);
|
||||
if (value !== undefined)
|
||||
prototype[name] = mixin[name];
|
||||
}
|
||||
}
|
||||
|
||||
/* copy class vars from the superclass */
|
||||
properties.__classvars__ = properties.__classvars__ || {};
|
||||
if (prototype.__classvars__)
|
||||
for (var key in prototype.__classvars__)
|
||||
if (!properties.__classvars__[key]) {
|
||||
var value = getOwnProperty(prototype.__classvars__, key);
|
||||
properties.__classvars__[key] = value;
|
||||
}
|
||||
|
||||
/* copy all properties over to the new prototype */
|
||||
for (var name in properties) {
|
||||
var value = getOwnProperty(properties, name);
|
||||
if (name === '__include__' ||
|
||||
value === undefined)
|
||||
continue;
|
||||
|
||||
prototype[name] = typeof value === 'function' && usesSuper(value) ?
|
||||
(function(meth, name) {
|
||||
return function() {
|
||||
var old_super = getOwnProperty(this, '$super');
|
||||
this.$super = super_prototype[name];
|
||||
try {
|
||||
return meth.apply(this, arguments);
|
||||
}
|
||||
finally {
|
||||
setOrUnset(this, '$super', old_super);
|
||||
}
|
||||
};
|
||||
})(value, name) : value
|
||||
}
|
||||
|
||||
var class_init = this.__class_init__ || function() {};
|
||||
var p_class_init = prototype.__class_init__ || function() {};
|
||||
delete prototype.__class_init__;
|
||||
var n_class_init = function() {
|
||||
class_init.apply(null, arguments);
|
||||
p_class_init.apply(null, arguments);
|
||||
}
|
||||
n_class_init(prototype);
|
||||
|
||||
/* dummy constructor */
|
||||
var instance = function() {
|
||||
if (disable_constructor)
|
||||
return;
|
||||
var proper_this = context === this ? cheapNew(arguments.callee) : this;
|
||||
if (proper_this.__init__)
|
||||
proper_this.__init__.apply(proper_this, arguments);
|
||||
proper_this.$class = instance;
|
||||
return proper_this;
|
||||
}
|
||||
|
||||
/* copy all class vars over of any */
|
||||
for (var key in properties.__classvars__) {
|
||||
var value = getOwnProperty(properties.__classvars__, key);
|
||||
if (value !== undefined)
|
||||
instance[key] = value;
|
||||
}
|
||||
|
||||
/* copy prototype and constructor over, reattach $extend and
|
||||
return the class */
|
||||
instance.prototype = prototype;
|
||||
instance.constructor = instance;
|
||||
instance.$extend = this.$extend;
|
||||
instance.$withData = this.$withData;
|
||||
instance.__class_init__ = n_class_init;
|
||||
return instance;
|
||||
};
|
||||
|
||||
/* instanciate with data functionality */
|
||||
Class.$withData = function(data) {
|
||||
var rv = cheapNew(this);
|
||||
for (var key in data) {
|
||||
var value = getOwnProperty(data, key);
|
||||
if (value !== undefined)
|
||||
rv[key] = value;
|
||||
}
|
||||
return rv;
|
||||
};
|
||||
|
||||
/* export the class */
|
||||
this.Class = Class;
|
||||
}).call(nova);
|
||||
// end of Armin Ronacher's code
|
||||
|
||||
var mixinId = 1;
|
||||
nova.Mixin = nova.Class.$extend({
|
||||
__init__: function() {
|
||||
this.__mixin_properties = {};
|
||||
this.__mixin_id = mixinId;
|
||||
mixinId++;
|
||||
this.__mixin_ids = {};
|
||||
this.__mixin_ids[this.__mixin_id] = true;
|
||||
_.each(_.toArray(arguments), function(el) {
|
||||
if (el instanceof nova.Mixin) {
|
||||
_.extend(this.__mixin_properties, el.__mixin_properties);
|
||||
_.extend(this.__mixin_ids, el.__mixin_ids);
|
||||
} else { // object
|
||||
_.extend(this.__mixin_properties, el)
|
||||
}
|
||||
}, this);
|
||||
_.extend(this, this.__mixin_properties);
|
||||
}
|
||||
});
|
||||
|
||||
nova.Interface = nova.Mixin.$extend({
|
||||
__init__: function() {
|
||||
var lst = [];
|
||||
_.each(_.toArray(arguments), function(el) {
|
||||
if (el instanceof nova.Interface) {
|
||||
lst.push(el);
|
||||
} else if (el instanceof nova.Mixin) {
|
||||
var tmp = new nova.Interface(el.__mixin_properties);
|
||||
tmp.__mixin_ids = el.__mixin_ids;
|
||||
lst.push(tmp);
|
||||
} else { // object
|
||||
var nprops = {};
|
||||
_.each(el, function(v, k) {
|
||||
nprops[k] = function() {
|
||||
throw new nova.NotImplementedError();
|
||||
};
|
||||
});
|
||||
lst.push(nprops);
|
||||
}
|
||||
});
|
||||
this.$super.apply(this, lst);
|
||||
}
|
||||
});
|
||||
|
||||
nova.hasMixin = function(object, mixin) {
|
||||
if (! object)
|
||||
return false;
|
||||
return (object.__mixin_ids || {})[mixin.__mixin_id] === true;
|
||||
};
|
||||
|
||||
var ErrorBase = function() {
|
||||
};
|
||||
ErrorBase.prototype = new Error();
|
||||
ErrorBase.$extend = nova.Class.$extend;
|
||||
ErrorBase.$withData = nova.Class.$withData;
|
||||
|
||||
nova.Error = ErrorBase.$extend({
|
||||
name: "nova.Error",
|
||||
defaultMessage: "",
|
||||
__init__: function(message) {
|
||||
this.message = message || this.defaultMessage;
|
||||
}
|
||||
});
|
||||
|
||||
nova.NotImplementedError = nova.Error.$extend({
|
||||
name: "nova.NotImplementedError",
|
||||
defaultMessage: "This method is not implemented"
|
||||
});
|
||||
|
||||
nova.InvalidArgumentError = nova.Error.$extend({
|
||||
name: "nova.InvalidArgumentError"
|
||||
});
|
||||
|
||||
/**
|
||||
* Mixin to express the concept of destroying an object.
|
||||
* When an object is destroyed, it should release any resource
|
||||
* it could have reserved before.
|
||||
*/
|
||||
nova.Destroyable = new nova.Mixin({
|
||||
__init__: function() {
|
||||
this.__destroyableDestroyed = false;
|
||||
},
|
||||
/**
|
||||
* Returns true if destroy() was called on the current object.
|
||||
*/
|
||||
isDestroyed : function() {
|
||||
return this.__destroyableDestroyed;
|
||||
},
|
||||
/**
|
||||
* Inform the object it should destroy itself, releasing any
|
||||
* resource it could have reserved.
|
||||
*/
|
||||
destroy : function() {
|
||||
this.__destroyableDestroyed = true;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Mixin to structure objects' life-cycles folowing a parent-children
|
||||
* relationship. Each object can a have a parent and multiple children.
|
||||
* When an object is destroyed, all its children are destroyed too.
|
||||
*/
|
||||
nova.Parented = new nova.Mixin(nova.Destroyable, {
|
||||
__parentedMixin : true,
|
||||
__init__: function() {
|
||||
nova.Destroyable.__init__.apply(this);
|
||||
this.__parentedChildren = [];
|
||||
this.__parentedParent = null;
|
||||
},
|
||||
/**
|
||||
* Set the parent of the current object. When calling this method, the
|
||||
* parent will also be informed and will return the current object
|
||||
* when its getChildren() method is called. If the current object did
|
||||
* already have a parent, it is unregistered before, which means the
|
||||
* previous parent will not return the current object anymore when its
|
||||
* getChildren() method is called.
|
||||
*/
|
||||
setParent : function(parent) {
|
||||
if (this.getParent()) {
|
||||
if (this.getParent().__parentedMixin) {
|
||||
this.getParent().__parentedChildren = _.without(this
|
||||
.getParent().getChildren(), this);
|
||||
}
|
||||
}
|
||||
this.__parentedParent = parent;
|
||||
if (parent && parent.__parentedMixin) {
|
||||
parent.__parentedChildren.push(this);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Return the current parent of the object (or null).
|
||||
*/
|
||||
getParent : function() {
|
||||
return this.__parentedParent;
|
||||
},
|
||||
/**
|
||||
* Return a list of the children of the current object.
|
||||
*/
|
||||
getChildren : function() {
|
||||
return _.clone(this.__parentedChildren);
|
||||
},
|
||||
destroy : function() {
|
||||
_.each(this.getChildren(), function(el) {
|
||||
el.destroy();
|
||||
});
|
||||
this.setParent(undefined);
|
||||
nova.Destroyable.destroy.apply(this);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Yes, we steal Backbone's events :)
|
||||
*
|
||||
* This class just handle the dispatching of events, it is not meant to be extended,
|
||||
* nor used directly. All integration with parenting and automatic unregistration of
|
||||
* events is done in the mixin EventDispatcher.
|
||||
*/
|
||||
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||
// Backbone may be freely distributed under the MIT license.
|
||||
// For all details and documentation:
|
||||
// http://backbonejs.org
|
||||
nova.internal.Events = nova.Class.$extend({
|
||||
on : function(events, callback, context) {
|
||||
var ev;
|
||||
events = events.split(/\s+/);
|
||||
var calls = this._callbacks || (this._callbacks = {});
|
||||
while (ev = events.shift()) {
|
||||
var list = calls[ev] || (calls[ev] = {});
|
||||
var tail = list.tail || (list.tail = list.next = {});
|
||||
tail.callback = callback;
|
||||
tail.context = context;
|
||||
list.tail = tail.next = {};
|
||||
}
|
||||
return this;
|
||||
},
|
||||
off : function(events, callback, context) {
|
||||
var ev, calls, node;
|
||||
if (!events) {
|
||||
delete this._callbacks;
|
||||
} else if (calls = this._callbacks) {
|
||||
events = events.split(/\s+/);
|
||||
while (ev = events.shift()) {
|
||||
node = calls[ev];
|
||||
delete calls[ev];
|
||||
if (!callback || !node)
|
||||
continue;
|
||||
while ((node = node.next) && node.next) {
|
||||
if (node.callback === callback
|
||||
&& (!context || node.context === context))
|
||||
continue;
|
||||
this.on(ev, node.callback, node.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
callbackList: function() {
|
||||
var lst = [];
|
||||
_.each(this._callbacks || {}, function(el, eventName) {
|
||||
var node = el;
|
||||
while ((node = node.next) && node.next) {
|
||||
lst.push([eventName, node.callback, node.context]);
|
||||
}
|
||||
});
|
||||
return lst;
|
||||
},
|
||||
trigger : function(events) {
|
||||
var event, node, calls, tail, args, all, rest;
|
||||
if (!(calls = this._callbacks))
|
||||
return this;
|
||||
all = calls['all'];
|
||||
(events = events.split(/\s+/)).push(null);
|
||||
// Save references to the current heads & tails.
|
||||
while (event = events.shift()) {
|
||||
if (all)
|
||||
events.push({
|
||||
next : all.next,
|
||||
tail : all.tail,
|
||||
event : event
|
||||
});
|
||||
if (!(node = calls[event]))
|
||||
continue;
|
||||
events.push({
|
||||
next : node.next,
|
||||
tail : node.tail
|
||||
});
|
||||
}
|
||||
rest = Array.prototype.slice.call(arguments, 1);
|
||||
while (node = events.pop()) {
|
||||
tail = node.tail;
|
||||
args = node.event ? [ node.event ].concat(rest) : rest;
|
||||
while ((node = node.next) !== tail) {
|
||||
node.callback.apply(node.context || this, args);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
// end of Backbone's events class
|
||||
|
||||
nova.EventDispatcher = new nova.Mixin(nova.Parented, {
|
||||
__eventDispatcherMixin: true,
|
||||
__init__: function() {
|
||||
nova.Parented.__init__.apply(this);
|
||||
this.__edispatcherEvents = new nova.internal.Events();
|
||||
this.__edispatcherRegisteredEvents = [];
|
||||
},
|
||||
on: function(events, dest, func) {
|
||||
var self = this;
|
||||
events = events.split(/\s+/);
|
||||
_.each(events, function(eventName) {
|
||||
self.__edispatcherEvents.on(eventName, func, dest);
|
||||
if (dest && dest.__eventDispatcherMixin) {
|
||||
dest.__edispatcherRegisteredEvents.push({name: eventName, func: func, source: self});
|
||||
}
|
||||
});
|
||||
return this;
|
||||
},
|
||||
off: function(events, dest, func) {
|
||||
var self = this;
|
||||
events = events.split(/\s+/);
|
||||
_.each(events, function(eventName) {
|
||||
self.__edispatcherEvents.off(eventName, func, dest);
|
||||
if (dest && dest.__eventDispatcherMixin) {
|
||||
dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function(el) {
|
||||
return !(el.name === eventName && el.func === func && el.source === self);
|
||||
});
|
||||
}
|
||||
});
|
||||
return this;
|
||||
},
|
||||
trigger: function(events) {
|
||||
this.__edispatcherEvents.trigger.apply(this.__edispatcherEvents, arguments);
|
||||
return this;
|
||||
},
|
||||
destroy: function() {
|
||||
var self = this;
|
||||
_.each(this.__edispatcherRegisteredEvents, function(event) {
|
||||
event.source.__edispatcherEvents.off(event.name, event.func, self);
|
||||
});
|
||||
this.__edispatcherRegisteredEvents = [];
|
||||
_.each(this.__edispatcherEvents.callbackList(), function(cal) {
|
||||
this.off(cal[0], cal[2], cal[1]);
|
||||
}, this);
|
||||
this.__edispatcherEvents.off();
|
||||
nova.Parented.destroy.apply(this);
|
||||
}
|
||||
});
|
||||
|
||||
nova.Properties = new nova.Mixin(nova.EventDispatcher, {
|
||||
__class_init__: function(proto) {
|
||||
var props = {};
|
||||
_.each(proto.__properties || {}, function(v, k) {
|
||||
props[k] = _.clone(v);
|
||||
});
|
||||
_.each(proto, function(v, k) {
|
||||
if (typeof v === "function") {
|
||||
var res = /^((?:get)|(?:set))([A-Z]\w*)$/.exec(k);
|
||||
if (! res)
|
||||
return;
|
||||
var name = res[2][0].toLowerCase() + res[2].slice(1);
|
||||
var prop = props[name] || (props[name] = {});
|
||||
prop[res[1]] = v;
|
||||
}
|
||||
});
|
||||
proto.__properties = props;
|
||||
},
|
||||
__init__: function() {
|
||||
nova.EventDispatcher.__init__.apply(this);
|
||||
this.__dynamicProperties = {};
|
||||
},
|
||||
set: function(arg1, arg2) {
|
||||
var self = this;
|
||||
var map;
|
||||
if (typeof arg1 === "string") {
|
||||
map = {};
|
||||
map[arg1] = arg2;
|
||||
} else {
|
||||
map = arg1;
|
||||
}
|
||||
var tmp_set = this.__props_setting;
|
||||
this.__props_setting = false;
|
||||
_.each(map, function(val, key) {
|
||||
var prop = self.__properties[key];
|
||||
if (prop) {
|
||||
if (! prop.set)
|
||||
throw new nova.InvalidArgumentError("Property " + key + " does not have a setter method.");
|
||||
prop.set.call(self, val);
|
||||
} else {
|
||||
self.fallbackSet(key, val);
|
||||
}
|
||||
});
|
||||
this.__props_setting = tmp_set;
|
||||
if (! this.__props_setting && this.__props_setted) {
|
||||
this.__props_setted = false;
|
||||
self.trigger("change", self);
|
||||
}
|
||||
},
|
||||
get: function(key) {
|
||||
var prop = this.__properties[key];
|
||||
if (prop) {
|
||||
if (! prop.get)
|
||||
throw new nova.InvalidArgumentError("Property " + key + " does not have a getter method.");
|
||||
return prop.get.call(this);
|
||||
} else {
|
||||
return this.fallbackGet(key);
|
||||
}
|
||||
},
|
||||
fallbackSet: function(key, val) {
|
||||
throw new nova.InvalidArgumentError("Property " + key + " is not defined.");
|
||||
},
|
||||
fallbackGet: function(key) {
|
||||
throw new nova.InvalidArgumentError("Property " + key + " is not defined.");
|
||||
},
|
||||
trigger: function(name) {
|
||||
nova.EventDispatcher.trigger.apply(this, arguments);
|
||||
if (/(\s|^)change\:.*/.exec(name)) {
|
||||
if (! this.__props_setting)
|
||||
this.trigger("change");
|
||||
else
|
||||
this.__props_setted = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
nova.DynamicProperties = new nova.Mixin(nova.Properties, {
|
||||
__init__: function() {
|
||||
nova.Properties.__init__.apply(this);
|
||||
this.__dynamicProperties = {};
|
||||
},
|
||||
fallbackSet: function(key, val) {
|
||||
var tmp = this.__dynamicProperties[key];
|
||||
if (tmp === val)
|
||||
return;
|
||||
this.__dynamicProperties[key] = val;
|
||||
this.trigger("change:" + key, this, {
|
||||
oldValue: tmp,
|
||||
newValue: val
|
||||
});
|
||||
},
|
||||
fallbackGet: function(key) {
|
||||
return this.__dynamicProperties[key];
|
||||
}
|
||||
});
|
||||
|
||||
nova.Widget = nova.Class.$extend({
|
||||
__include__ : [nova.DynamicProperties],
|
||||
tagName: 'div',
|
||||
className: '',
|
||||
attributes: {},
|
||||
events: {},
|
||||
__init__: function(parent) {
|
||||
nova.Properties.__init__.apply(this);
|
||||
this.__widget_element = $(document.createElement(this.tagName));
|
||||
this.$().addClass(this.className);
|
||||
_.each(this.attributes, function(val, key) {
|
||||
this.$().attr(key, val);
|
||||
}, this);
|
||||
_.each(this.events, function(val, key) {
|
||||
key = key.split(" ");
|
||||
val = _.bind(typeof val === "string" ? this[val] : val, this);
|
||||
if (key.length > 1) {
|
||||
this.$().on(key[0], key[1], val);
|
||||
} else {
|
||||
this.$().on(key[0], val);
|
||||
}
|
||||
}, this);
|
||||
|
||||
this.setParent(parent);
|
||||
},
|
||||
$: function(attr) {
|
||||
if (attr)
|
||||
return this.__widget_element.find.apply(this.__widget_element, arguments);
|
||||
else
|
||||
return this.__widget_element;
|
||||
},
|
||||
/**
|
||||
* Destroys the current widget, also destroys all its children before destroying itself.
|
||||
*/
|
||||
destroy: function() {
|
||||
_.each(this.getChildren(), function(el) {
|
||||
el.destroy();
|
||||
});
|
||||
this.$().remove();
|
||||
nova.Properties.destroy.apply(this);
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and appends it to the given jQuery object or Widget.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
appendTo: function(target) {
|
||||
this.$().appendTo(target);
|
||||
return this.render();
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and prepends it to the given jQuery object or Widget.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
prependTo: function(target) {
|
||||
this.$().prependTo(target);
|
||||
return this.render();
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and inserts it after to the given jQuery object or Widget.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
insertAfter: function(target) {
|
||||
this.$().insertAfter(target);
|
||||
return this.render();
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and inserts it before to the given jQuery object or Widget.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
insertBefore: function(target) {
|
||||
this.$().insertBefore(target);
|
||||
return this.render();
|
||||
},
|
||||
/**
|
||||
* Renders the current widget and replaces the given jQuery object.
|
||||
*
|
||||
* @param target A jQuery object or a Widget instance.
|
||||
*/
|
||||
replace: function(target) {
|
||||
this.$().replace(target);
|
||||
return this.render();
|
||||
},
|
||||
/**
|
||||
* This is the method to implement to render the Widget.
|
||||
*/
|
||||
render: function() {}
|
||||
});
|
||||
|
||||
/*
|
||||
Nova Template Engine
|
||||
*/
|
||||
var escape_ = function(text) {
|
||||
return JSON.stringify(text);
|
||||
}
|
||||
var indent_ = function(txt) {
|
||||
var tmp = _.map(txt.split("\n"), function(x) { return " " + x; });
|
||||
tmp.pop();
|
||||
tmp.push("");
|
||||
return tmp.join("\n");
|
||||
};
|
||||
var tparams = {
|
||||
def_begin: /<%\s*def\s+(?:name=(?:(?:"(.+?)")|(?:'(.+?)')))\s*>/g,
|
||||
def_end: /<\/%\s*def\s*>/g,
|
||||
comment_multi_begin: /<%\s*doc\s*>/g,
|
||||
comment_multi_end: /<\/%\s*doc\s*>/g,
|
||||
eval_long_begin: /<%/g,
|
||||
eval_long_end: /%>/g,
|
||||
eval_short_begin: /(?:^|\n)[[ \t]*%(?!{)/g,
|
||||
eval_short_end: /\n|$/g,
|
||||
escape_begin: /\${/g,
|
||||
interpolate_begin: /%{/g,
|
||||
comment_begin: /##/g,
|
||||
comment_end: /\n|$/g
|
||||
};
|
||||
// /<%\s*def\s+(?:name=(?:"(.+?)"))\s*%>([\s\S]*?)<%\s*def\s*%>/g
|
||||
var allbegin = new RegExp(
|
||||
"((?:\\\\)*)(" +
|
||||
"(" + tparams.def_begin.source + ")|" +
|
||||
"(" + tparams.def_end.source + ")|" +
|
||||
"(" + tparams.comment_multi_begin.source + ")|" +
|
||||
"(" + tparams.eval_long_begin.source + ")|" +
|
||||
"(" + tparams.interpolate_begin.source + ")|" +
|
||||
"(" + tparams.eval_short_begin.source + ")|" +
|
||||
"(" + tparams.escape_begin.source + ")|" +
|
||||
"(" + tparams.comment_begin.source + ")" +
|
||||
")"
|
||||
, "g");
|
||||
allbegin.global = true;
|
||||
var regexes = {
|
||||
slashes: 1,
|
||||
match: 2,
|
||||
def_begin: 3,
|
||||
def_name1: 4,
|
||||
def_name2: 5,
|
||||
def_end: 6,
|
||||
comment_multi_begin: 7,
|
||||
eval_long: 8,
|
||||
interpolate: 9,
|
||||
eval_short: 10,
|
||||
escape: 11,
|
||||
comment: 12
|
||||
};
|
||||
var regex_count = 4;
|
||||
|
||||
var compileTemplate = function(text, options) {
|
||||
options = _.extend({start: 0, indent: true}, options);
|
||||
start = options.start;
|
||||
var source = "";
|
||||
var current = start;
|
||||
allbegin.lastIndex = current;
|
||||
var text_end = text.length;
|
||||
var restart = end;
|
||||
var found;
|
||||
var functions = [];
|
||||
var indent = options.indent ? indent_ : function (txt) { return txt; };
|
||||
var rmWhite = options.removeWhitespaces ? function(txt) {
|
||||
if (! txt)
|
||||
return txt;
|
||||
txt = _.map(txt.split("\n"), function(x) { return x.trim() });
|
||||
var last = txt.pop();
|
||||
txt = _.reject(txt, function(x) { return !x });
|
||||
txt.push(last);
|
||||
return txt.join("\n") || "\n";
|
||||
} : function(x) { return x };
|
||||
while (found = allbegin.exec(text)) {
|
||||
var to_add = rmWhite(text.slice(current, found.index));
|
||||
source += to_add ? "__p+=" + escape_(to_add) + ";\n" : '';
|
||||
current = found.index;
|
||||
|
||||
// slash escaping handling
|
||||
var slashes = found[regexes.slashes] || "";
|
||||
var nbr = slashes.length;
|
||||
var nslash = slashes.slice(0, Math.floor(nbr / 2));
|
||||
source += nbr !== 0 ? "__p+=" + escape_(nslash) + ";\n" : "";
|
||||
if (nbr % 2 !== 0) {
|
||||
source += "__p+=" + escape_(found[regexes.match]) + ";\n";
|
||||
current = found.index + found[0].length;
|
||||
allbegin.lastIndex = current;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (found[regexes.def_begin]) {
|
||||
var sub_compile = compileTemplate(text, _.extend({}, options, {start: found.index + found[0].length}));
|
||||
var name = (found[regexes.def_name1] || found[regexes.def_name2]);
|
||||
source += "var " + name + " = function(context) {\n" + indent(sub_compile.header + sub_compile.source
|
||||
+ sub_compile.footer) + "}\n";
|
||||
functions.push(name);
|
||||
current = sub_compile.end;
|
||||
} else if (found[regexes.def_end]) {
|
||||
text_end = found.index;
|
||||
restart = found.index + found[0].length;
|
||||
break;
|
||||
} else if (found[regexes.comment_multi_begin]) {
|
||||
tparams.comment_multi_end.lastIndex = found.index + found[0].length;
|
||||
var end = tparams.comment_multi_end.exec(text);
|
||||
if (!end)
|
||||
throw new Error("<%doc> without corresponding </%doc>");
|
||||
current = end.index + end[0].length;
|
||||
} else if (found[regexes.eval_long]) {
|
||||
tparams.eval_long_end.lastIndex = found.index + found[0].length;
|
||||
var end = tparams.eval_long_end.exec(text);
|
||||
if (!end)
|
||||
throw new Error("<% without matching %>");
|
||||
var code = text.slice(found.index + found[0].length, end.index);
|
||||
code = _(code.split("\n")).chain().map(function(x) { return x.trim() })
|
||||
.reject(function(x) { return !x }).value().join("\n");
|
||||
source += code + "\n";
|
||||
current = end.index + end[0].length;
|
||||
} else if (found[regexes.interpolate]) {
|
||||
var braces = /{|}/g;
|
||||
braces.lastIndex = found.index + found[0].length;
|
||||
var b_count = 1;
|
||||
var brace;
|
||||
while (brace = braces.exec(text)) {
|
||||
if (brace[0] === "{")
|
||||
b_count++;
|
||||
else {
|
||||
b_count--;
|
||||
}
|
||||
if (b_count === 0)
|
||||
break;
|
||||
}
|
||||
if (b_count !== 0)
|
||||
throw new Error("%{ without a matching }");
|
||||
source += "__p+=" + text.slice(found.index + found[0].length, brace.index) + ";\n"
|
||||
current = brace.index + brace[0].length;
|
||||
} else if (found[regexes.eval_short]) {
|
||||
tparams.eval_short_end.lastIndex = found.index + found[0].length;
|
||||
var end = tparams.eval_short_end.exec(text);
|
||||
if (!end)
|
||||
throw new Error("impossible state!!");
|
||||
source += text.slice(found.index + found[0].length, end.index).trim() + "\n";
|
||||
current = end.index;
|
||||
} else if (found[regexes.escape]) {
|
||||
var braces = /{|}/g;
|
||||
braces.lastIndex = found.index + found[0].length;
|
||||
var b_count = 1;
|
||||
var brace;
|
||||
while (brace = braces.exec(text)) {
|
||||
if (brace[0] === "{")
|
||||
b_count++;
|
||||
else {
|
||||
b_count--;
|
||||
}
|
||||
if (b_count === 0)
|
||||
break;
|
||||
}
|
||||
if (b_count !== 0)
|
||||
throw new Error("${ without a matching }");
|
||||
source += "__p+=_.escape(" + text.slice(found.index + found[0].length, brace.index) + ");\n"
|
||||
current = brace.index + brace[0].length;
|
||||
} else { // comment
|
||||
tparams.comment_end.lastIndex = found.index + found[0].length;
|
||||
var end = tparams.comment_end.exec(text);
|
||||
if (!end)
|
||||
throw new Error("impossible state!!");
|
||||
current = end.index + end[0].length;
|
||||
}
|
||||
allbegin.lastIndex = current;
|
||||
}
|
||||
var to_add = rmWhite(text.slice(current, text_end));
|
||||
source += to_add ? "__p+=" + escape_(to_add) + ";\n" : "";
|
||||
|
||||
var header = "var __p = ''; var print = function() { __p+=Array.prototype.join.call(arguments, '') };\n" +
|
||||
"with (context || {}) {\n";
|
||||
var footer = "}\nreturn __p;\n";
|
||||
source = indent(source);
|
||||
|
||||
return {
|
||||
header: header,
|
||||
source: source,
|
||||
footer: footer,
|
||||
end: restart,
|
||||
functions: functions,
|
||||
};
|
||||
};
|
||||
|
||||
nova.TemplateEngine = nova.Class.$extend({
|
||||
__init__: function() {
|
||||
this.resetEnvironment();
|
||||
this.options = {
|
||||
includeInDom: $ ? true : false,
|
||||
indent: true,
|
||||
removeWhitespaces: true,
|
||||
};
|
||||
},
|
||||
loadFile: function(filename) {
|
||||
var self = this;
|
||||
return $.get(filename).pipe(function(content) {
|
||||
return self.loadFileContent(content);
|
||||
});
|
||||
},
|
||||
loadFileContent: function(file_content) {
|
||||
var code = this.compileFile(file_content);
|
||||
|
||||
if (this.options.includeInDom) {
|
||||
var varname = _.uniqueId("novajstemplate");
|
||||
var previous = window[varname];
|
||||
code = "window." + varname + " = " + code + ";";
|
||||
var def = $.Deferred();
|
||||
var script = document.createElement("script");
|
||||
script.type = "text/javascript";
|
||||
script.text = code;
|
||||
$("head")[0].appendChild(script);
|
||||
$(script).ready(function() {
|
||||
def.resolve();
|
||||
});
|
||||
def.then(_.bind(function() {
|
||||
var tmp = window[varname];
|
||||
window[varname] = previous;
|
||||
this.includeTemplates(tmp);
|
||||
}, this));
|
||||
return def;
|
||||
} else {
|
||||
console.log("return (" + code + ")(context);");
|
||||
return this.includeTemplates(new Function('context', "return (" + code + ")(context);"));
|
||||
}
|
||||
},
|
||||
compileFile: function(file_content) {
|
||||
var result = compileTemplate(file_content, _.extend({}, this.options));
|
||||
var to_append = "";
|
||||
_.each(result.functions, function(name) {
|
||||
to_append += name + ": " + name + ",\n";
|
||||
}, this);
|
||||
to_append = this.options.indent ? indent_(to_append) : to_append;
|
||||
to_append = "return {\n" + to_append + "};\n";
|
||||
to_append = this.options.indent ? indent_(to_append) : to_append;
|
||||
var code = "function(context) {\n" + result.header +
|
||||
result.source + to_append + result.footer + "}\n";
|
||||
return code;
|
||||
},
|
||||
includeTemplates: function(fct) {
|
||||
var add = _.extend({engine: this}, this._env);
|
||||
var functions = fct(add);
|
||||
_.each(functions, function(func, name) {
|
||||
if (this[name])
|
||||
throw new Error("The template '" + name + "' is already defined");
|
||||
this[name] = func;
|
||||
}, this);
|
||||
},
|
||||
buildTemplate: function(text) {
|
||||
var comp = compileTemplate(text, _.extend({}, this.options));
|
||||
var result = comp.header + comp.source + comp.footer;
|
||||
var add = _.extend({engine: this}, this._env);
|
||||
var func = new Function('context', result);
|
||||
return function(data) {
|
||||
return func.call(this, _.extend(add, data));
|
||||
};
|
||||
},
|
||||
eval: function(text, context) {
|
||||
return this.buildTemplate(text)(context);
|
||||
},
|
||||
resetEnvironment: function(nenv) {
|
||||
this._env = {_: _};
|
||||
this.extendEnvironment(nenv);
|
||||
},
|
||||
extendEnvironment: function(env) {
|
||||
_.extend(this._env, env || {});
|
||||
},
|
||||
});
|
||||
|
||||
return nova;
|
||||
};
|
||||
})();
|
||||
@@ -1,362 +0,0 @@
|
||||
/*
|
||||
Copyright (c) 2013, OpenERP
|
||||
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.
|
||||
*/
|
||||
|
||||
define(["underscore", "jquery", "nova"], function(_, $, nova) {
|
||||
|
||||
var oeclient = {};
|
||||
|
||||
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)));
|
||||
});
|
||||
};
|
||||
|
||||
oeclient.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',
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
oeclient.jsonpRpc = function(url, fct_name, params, settings) {
|
||||
return genericJsonRPC(fct_name, params, function(data) {
|
||||
return $.ajax(url, _.extend({}, settings, {
|
||||
url: url,
|
||||
dataType: 'jsonp',
|
||||
jsonp: 'jsonp',
|
||||
type: 'GET',
|
||||
cache: false,
|
||||
data: {r: JSON.stringify(data)},
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
oeclient.Connector = nova.Class.$extend({
|
||||
getService: function(serviceName) {
|
||||
return new oeclient.Service(this, serviceName);
|
||||
},
|
||||
});
|
||||
|
||||
oeclient.JsonRPCConnector = oeclient.Connector.$extend({
|
||||
__init__: function(url) {
|
||||
this.url = url;
|
||||
},
|
||||
call: function(sub_url, content) {
|
||||
return oeclient.jsonRpc(this.url + sub_url, "call", content);
|
||||
},
|
||||
send: function(serviceName, method, args) {
|
||||
return this.call("/jsonrpc", {"service": serviceName, "method": method, "args": args});
|
||||
},
|
||||
});
|
||||
|
||||
oeclient.JsonpRPCConnector = oeclient.JsonRPCConnector.$extend({
|
||||
call: function(sub_url, content) {
|
||||
return oeclient.jsonpRpc(this.url + sub_url, "call", content);
|
||||
},
|
||||
});
|
||||
|
||||
oeclient.Service = nova.Class.$extend({
|
||||
__init__: function(connector, serviceName) {
|
||||
this.connector = connector;
|
||||
this.serviceName = serviceName;
|
||||
},
|
||||
call: function(method, args) {
|
||||
return this.connector.send(this.serviceName, method, args);
|
||||
},
|
||||
});
|
||||
|
||||
oeclient.AuthenticationError = nova.Error.$extend({
|
||||
name: "oeclient.AuthenticationError",
|
||||
defaultMessage: "An error occured during authentication."
|
||||
});
|
||||
|
||||
oeclient.Connection = nova.Class.$extend({
|
||||
__init__: function(connector, database, login, password, userId) {
|
||||
this.connector = connector;
|
||||
this.setLoginInfo(database, login, password, userId);
|
||||
this.userContext = null;
|
||||
},
|
||||
setLoginInfo: function(database, login, password, userId) {
|
||||
this.database = database;
|
||||
this.login = login;
|
||||
this.password = password;
|
||||
this.userId = userId;
|
||||
},
|
||||
checkLogin: function(force) {
|
||||
force = force === undefined ? true: force;
|
||||
if (this.userId && ! force)
|
||||
return $.when();
|
||||
|
||||
if (! this.database || ! this.login || ! this.password)
|
||||
throw new oeclient.AuthenticationError();
|
||||
|
||||
return this.getService("common").call("login", [this.database, this.login, this.password])
|
||||
.then(_.bind(function(result) {
|
||||
this.userId = result;
|
||||
if (! this.userId) {
|
||||
console.error("Authentication failure");
|
||||
return $.Deferred().reject({message:"Authentication failure"});
|
||||
}
|
||||
}, this));
|
||||
},
|
||||
getUserContext: function() {
|
||||
if (! this.userContext) {
|
||||
return this.getModel("res.users").call("context_get").then(_.bind(function(result) {
|
||||
this.userContext = result;
|
||||
return this.userContext;
|
||||
}, this));
|
||||
}
|
||||
return $.when(this.userContext);
|
||||
},
|
||||
getModel: function(modelName) {
|
||||
return new oeclient.Model(this, modelName);
|
||||
},
|
||||
getService: function(serviceName) {
|
||||
return this.connector.getService(serviceName);
|
||||
},
|
||||
});
|
||||
|
||||
oeclient.Model = nova.Class.$extend({
|
||||
__init__: function(connection, modelName) {
|
||||
this.connection = connection;
|
||||
this.modelName = modelName;
|
||||
},
|
||||
call: function(method, args, kw) {
|
||||
return this.connection.checkLogin().then(_.bind(function() {
|
||||
return this.connection.getService("object").call("execute_kw", [
|
||||
this.connection.database,
|
||||
this.connection.userId,
|
||||
this.connection.password,
|
||||
this.modelName,
|
||||
method,
|
||||
args || [],
|
||||
kw || {},
|
||||
]);
|
||||
}, this));
|
||||
},
|
||||
search_read: function(domain, fields, offset, limit, order, context) {
|
||||
return this.call("search", [domain || [], offset || 0, limit || false, order || false, context || {}]).then(_.bind(function(record_ids) {
|
||||
if (! record_ids) {
|
||||
return [];
|
||||
}
|
||||
return this.call("read", [record_ids, fields || [], context || {}]).then(function(records) {
|
||||
var index = {};
|
||||
_.each(records, function(r) {
|
||||
index[r.id] = r;
|
||||
});
|
||||
var res = [];
|
||||
_.each(record_ids, function(id) {
|
||||
if (index[id])
|
||||
res.push(index[id]);
|
||||
});
|
||||
return res;
|
||||
});
|
||||
}, this));
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Converts a string to a Date javascript object using OpenERP's
|
||||
* datetime string format (exemple: '2011-12-01 15:12:35').
|
||||
*
|
||||
* The time zone is assumed to be UTC (standard for OpenERP 6.1)
|
||||
* and will be converted to the browser's time zone.
|
||||
*
|
||||
* @param {String} str A string representing a datetime.
|
||||
* @returns {Date}
|
||||
*/
|
||||
oeclient.str_to_datetime = function(str) {
|
||||
if(!str) {
|
||||
return str;
|
||||
}
|
||||
var regex = /^(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d(?:\.\d+)?)$/;
|
||||
var res = regex.exec(str);
|
||||
if ( !res ) {
|
||||
throw new Error("'" + str + "' is not a valid datetime");
|
||||
}
|
||||
var tmp = new Date();
|
||||
tmp.setUTCFullYear(parseFloat(res[1]));
|
||||
tmp.setUTCMonth(parseFloat(res[2]) - 1);
|
||||
tmp.setUTCDate(parseFloat(res[3]));
|
||||
tmp.setUTCHours(parseFloat(res[4]));
|
||||
tmp.setUTCMinutes(parseFloat(res[5]));
|
||||
tmp.setUTCSeconds(parseFloat(res[6]));
|
||||
return tmp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a string to a Date javascript object using OpenERP's
|
||||
* date string format (exemple: '2011-12-01').
|
||||
*
|
||||
* As a date is not subject to time zones, we assume it should be
|
||||
* represented as a Date javascript object at 00:00:00 in the
|
||||
* time zone of the browser.
|
||||
*
|
||||
* @param {String} str A string representing a date.
|
||||
* @returns {Date}
|
||||
*/
|
||||
oeclient.str_to_date = function(str) {
|
||||
if(!str) {
|
||||
return str;
|
||||
}
|
||||
var regex = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
|
||||
var res = regex.exec(str);
|
||||
if ( !res ) {
|
||||
throw new Error("'" + str + "' is not a valid date");
|
||||
}
|
||||
var tmp = new Date();
|
||||
tmp.setFullYear(parseFloat(res[1]));
|
||||
tmp.setMonth(parseFloat(res[2]) - 1);
|
||||
tmp.setDate(parseFloat(res[3]));
|
||||
tmp.setHours(0);
|
||||
tmp.setMinutes(0);
|
||||
tmp.setSeconds(0);
|
||||
return tmp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a string to a Date javascript object using OpenERP's
|
||||
* time string format (exemple: '15:12:35').
|
||||
*
|
||||
* The OpenERP times are supposed to always be naive times. We assume it is
|
||||
* represented using a javascript Date with a date 1 of January 1970 and a
|
||||
* time corresponding to the meant time in the browser's time zone.
|
||||
*
|
||||
* @param {String} str A string representing a time.
|
||||
* @returns {Date}
|
||||
*/
|
||||
oeclient.str_to_time = function(str) {
|
||||
if(!str) {
|
||||
return str;
|
||||
}
|
||||
var regex = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)$/;
|
||||
var res = regex.exec(str);
|
||||
if ( !res ) {
|
||||
throw new Error("'" + str + "' is not a valid time");
|
||||
}
|
||||
debugger;
|
||||
var tmp = new Date();
|
||||
tmp.setFullYear(1970);
|
||||
tmp.setMonth(0);
|
||||
tmp.setDate(1);
|
||||
tmp.setHours(parseFloat(res[1]));
|
||||
tmp.setMinutes(parseFloat(res[2]));
|
||||
tmp.setSeconds(parseFloat(res[3]));
|
||||
return tmp;
|
||||
};
|
||||
|
||||
/*
|
||||
* Left-pad provided arg 1 with zeroes until reaching size provided by second
|
||||
* argument.
|
||||
*
|
||||
* @param {Number|String} str value to pad
|
||||
* @param {Number} size size to reach on the final padded value
|
||||
* @returns {String} padded string
|
||||
*/
|
||||
var zpad = function(str, size) {
|
||||
str = "" + str;
|
||||
return new Array(size - str.length + 1).join('0') + str;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a Date javascript object to a string using OpenERP's
|
||||
* datetime string format (exemple: '2011-12-01 15:12:35').
|
||||
*
|
||||
* The time zone of the Date object is assumed to be the one of the
|
||||
* browser and it will be converted to UTC (standard for OpenERP 6.1).
|
||||
*
|
||||
* @param {Date} obj
|
||||
* @returns {String} A string representing a datetime.
|
||||
*/
|
||||
oeclient.datetime_to_str = function(obj) {
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
return zpad(obj.getUTCFullYear(),4) + "-" + zpad(obj.getUTCMonth() + 1,2) + "-"
|
||||
+ zpad(obj.getUTCDate(),2) + " " + zpad(obj.getUTCHours(),2) + ":"
|
||||
+ zpad(obj.getUTCMinutes(),2) + ":" + zpad(obj.getUTCSeconds(),2);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a Date javascript object to a string using OpenERP's
|
||||
* date string format (exemple: '2011-12-01').
|
||||
*
|
||||
* As a date is not subject to time zones, we assume it should be
|
||||
* represented as a Date javascript object at 00:00:00 in the
|
||||
* time zone of the browser.
|
||||
*
|
||||
* @param {Date} obj
|
||||
* @returns {String} A string representing a date.
|
||||
*/
|
||||
oeclient.date_to_str = function(obj) {
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
return zpad(obj.getFullYear(),4) + "-" + zpad(obj.getMonth() + 1,2) + "-"
|
||||
+ zpad(obj.getDate(),2);
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a Date javascript object to a string using OpenERP's
|
||||
* time string format (exemple: '15:12:35').
|
||||
*
|
||||
* The OpenERP times are supposed to always be naive times. We assume it is
|
||||
* represented using a javascript Date with a date 1 of January 1970 and a
|
||||
* time corresponding to the meant time in the browser's time zone.
|
||||
*
|
||||
* @param {Date} obj
|
||||
* @returns {String} A string representing a time.
|
||||
*/
|
||||
oeclient.time_to_str = function(obj) {
|
||||
if (!obj) {
|
||||
return false;
|
||||
}
|
||||
return zpad(obj.getHours(),2) + ":" + zpad(obj.getMinutes(),2) + ":"
|
||||
+ zpad(obj.getSeconds(),2);
|
||||
};
|
||||
|
||||
return oeclient;
|
||||
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
import sys
|
||||
import json
|
||||
|
||||
file_name = sys.argv[1]
|
||||
function_name = sys.argv[2]
|
||||
|
||||
with open(file_name) as file_:
|
||||
content = file_.read()
|
||||
|
||||
print "window.%s(%s);" % (function_name, json.dumps(content))
|
||||
|
||||
+123
-83
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* achtung 0.3.0
|
||||
*
|
||||
* achtung %%VERSION%%
|
||||
*
|
||||
* Growl-like notifications for jQuery
|
||||
*
|
||||
* Copyright (c) 2009 Josh Varner <josh@voxwerk.com>
|
||||
@@ -11,10 +11,10 @@
|
||||
* 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
|
||||
@@ -29,43 +29,138 @@
|
||||
* @author Josh Varner <josh@voxwerk.com>
|
||||
*/
|
||||
|
||||
#achtung-overlay {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
/* IE 6 doesn't support position: fixed */
|
||||
* html #achtung-overlay {
|
||||
position:absolute;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* IE6 includes padding in width */
|
||||
* html .achtung {
|
||||
width: 280px;
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
#achtung-overlay {
|
||||
#achtung-wrapper {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
width: 280px;
|
||||
z-index:50;
|
||||
padding: 25px 30px 10px 10px;
|
||||
}
|
||||
|
||||
.achtung {
|
||||
display:none;
|
||||
display: none;
|
||||
float: right;
|
||||
clear: right;
|
||||
margin-bottom: 8px;
|
||||
padding: 15px 15px;
|
||||
background-color: #000;
|
||||
color: white;
|
||||
width: 250px;
|
||||
font-weight: bold;
|
||||
position:relative;
|
||||
overflow: hidden;
|
||||
-moz-box-shadow: #aaa 1px 1px 2px;
|
||||
-webkit-box-shadow: #aaa 1px 1px 2px;
|
||||
box-shadow: #aaa 1px 1px 2px;
|
||||
-moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px;
|
||||
padding: 15px;
|
||||
background: #000;
|
||||
background: rgba(0,0,0,.95);
|
||||
color: white;
|
||||
width: 230px;
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
font-size: 1.05em;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-shadow: -1px -1px 0 rgba(0,0,0,.3);
|
||||
box-shadow: rgba(0,0,0,.3) 0 1px 4px;
|
||||
-moz-box-shadow: rgba(0,0,0,.3) 0 1px 4px;
|
||||
-webkit-box-shadow: rgba(0,0,0,.3) 0 1px 4px;
|
||||
border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
opacity: 1.0;
|
||||
filter:Alpha(Opacity=100);
|
||||
}
|
||||
|
||||
.achtung-default {
|
||||
background: #000;
|
||||
background: -moz-linear-gradient(top, rgba(150,150,150,.9), rgba(120,120,120,.9) 70%);
|
||||
background: -webkit-gradient(linear, left top, left bottom,
|
||||
from(rgba(150,150,150,.9)),
|
||||
color-stop(70%, rgba(120,120,120,.9)),
|
||||
to(rgba(120,120,120,.9))
|
||||
) no-repeat;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.achtung .achtung-message-icon {
|
||||
float: left;
|
||||
margin: 0 .8em 0 -.5em;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.achtung .ui-icon.achtung-close-button {
|
||||
overflow: hidden;
|
||||
float: right;
|
||||
position: relative;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
cursor: pointer;
|
||||
background-image: url('images/ui-icons_cccccc_256x240.png');
|
||||
}
|
||||
|
||||
.achtung .ui-icon.achtung-close-button:hover {
|
||||
background-image: url('images/ui-icons_ffffff_256x240.png');
|
||||
}
|
||||
|
||||
/* Slightly darker for these colors (readability) */
|
||||
.achtungSuccess, .achtungFail, .achtungWait {
|
||||
/* Note that if using show/hide animations, IE will lose
|
||||
this setting */
|
||||
opacity: .85;
|
||||
filter:Alpha(Opacity=85);
|
||||
opacity: 1.0;
|
||||
filter:Alpha(Opacity=100);
|
||||
}
|
||||
|
||||
.achtungSuccess {
|
||||
background: #6a5;
|
||||
background: #6a5 -moz-linear-gradient(top, #8c7, #6a5 70%);
|
||||
background: #6a5 -webkit-gradient(linear, left top, left bottom,
|
||||
from(#8c7),
|
||||
color-stop(70%, #6a5),
|
||||
to(#6a5)
|
||||
) no-repeat;
|
||||
}
|
||||
.achtungFail {
|
||||
background: #a55;
|
||||
background: #a55 -moz-linear-gradient(top, #c66, #a44 70%);
|
||||
background: #789 -webkit-gradient(linear, left top, left bottom,
|
||||
from(#c66),
|
||||
color-stop(70%, #a44),
|
||||
to(#a44)
|
||||
) no-repeat;
|
||||
}
|
||||
.achtungWait {
|
||||
background: #789;
|
||||
background: #789 -moz-linear-gradient(top, #89a, #678 70%);
|
||||
background: #789 -webkit-gradient(linear, left top, left bottom,
|
||||
from(#89a),
|
||||
color-stop(70%, #678),
|
||||
to(#678)
|
||||
) no-repeat;
|
||||
}
|
||||
|
||||
.achtungSuccess .ui-icon.achtung-close-button,
|
||||
.achtungFail .ui-icon.achtung-close-button {
|
||||
background-image: url('images/ui-icons_444444_256x240.png');
|
||||
}
|
||||
|
||||
.achtungSuccess .ui-icon.achtung-close-button:hover,
|
||||
.achtungFail .ui-icon.achtung-close-button:hover {
|
||||
background-image: url('images/ui-icons_ffffff_256x240.png');
|
||||
}
|
||||
|
||||
.achtung .wait-icon {
|
||||
background-image: url('images/wait.gif');
|
||||
}
|
||||
|
||||
.achtung .achtung-message {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,8 +169,8 @@
|
||||
* Can (and should) be removed if you are already loading the jQuery UI CSS
|
||||
* to reduce payload size.
|
||||
*/
|
||||
.ui-icon { display: block; overflow: hidden; background-repeat: no-repeat; }
|
||||
.ui-icon { width: 16px; height: 16px; }
|
||||
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
|
||||
.ui-icon { width: 16px; height: 16px; background-image: url('images/ui-icons_222222_256x240.png'); }
|
||||
.ui-icon-carat-1-n { background-position: 0 0; }
|
||||
.ui-icon-carat-1-ne { background-position: -16px 0; }
|
||||
.ui-icon-carat-1-e { background-position: -32px 0; }
|
||||
@@ -249,58 +344,3 @@
|
||||
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
|
||||
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
|
||||
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
|
||||
|
||||
.achtung .achtung-message-icon {
|
||||
margin-top: 0px;
|
||||
margin-left: -.5em;
|
||||
margin-right: .5em;
|
||||
float: left;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.achtung .ui-icon.achtung-close-button {
|
||||
float: right;
|
||||
margin-right: -8px;
|
||||
margin-top: -12px;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.achtung .ui-icon.achtung-close-button:after {
|
||||
content: "x"
|
||||
}
|
||||
|
||||
/* Slightly darker for these colors (readability) */
|
||||
.achtungSuccess, .achtungFail, .achtungWait {
|
||||
/* Note that if using show/hide animations, IE will lose
|
||||
this setting */
|
||||
opacity: .93; filter:Alpha(Opacity=93);
|
||||
}
|
||||
|
||||
.achtungSuccess {
|
||||
background-color: #4DB559;
|
||||
}
|
||||
|
||||
.achtungFail {
|
||||
background-color: #D64450;
|
||||
}
|
||||
|
||||
.achtungWait {
|
||||
background-color: #658093;
|
||||
}
|
||||
|
||||
.achtungSuccess .ui-icon.achtung-close-button,
|
||||
.achtungFail .ui-icon.achtung-close-button {
|
||||
}
|
||||
|
||||
.achtungSuccess .ui-icon.achtung-close-button-hover,
|
||||
.achtungFail .ui-icon.achtung-close-button-hover {
|
||||
}
|
||||
|
||||
.achtung .wait-icon {
|
||||
}
|
||||
|
||||
.achtung .achtung-message {
|
||||
display: inline;
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/**
|
||||
* achtung %%VERSION%%
|
||||
*
|
||||
* Growl-like notifications for jQuery
|
||||
*
|
||||
* Copyright (c) 2009 Josh Varner <josh@voxwerk.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/mit-license.php
|
||||
* @author Josh Varner <josh@voxwerk.com>
|
||||
*/
|
||||
|
||||
/*jslint browser:true, white:false, onevar:false, nomen:false, bitwise:false, plusplus:false, immed: false */
|
||||
/*globals window, jQuery */
|
||||
(function ($) {
|
||||
|
||||
var widgetName = 'achtung';
|
||||
|
||||
/**
|
||||
* This is based on the jQuery UI $.widget code. I would have just made this
|
||||
* a $.widget but I didn't want the jQuery UI dependency.
|
||||
*/
|
||||
$.fn.achtung = function (options) {
|
||||
var isMethodCall = (typeof options === 'string'),
|
||||
args = Array.prototype.slice.call(arguments, isMethodCall ? 1 : 0);
|
||||
|
||||
// handle initialization and non-getter methods
|
||||
return this.each(function () {
|
||||
// prevent calls to internal methods
|
||||
if (isMethodCall && options.substring(0, 1) === '_') {
|
||||
return;
|
||||
}
|
||||
|
||||
var instance = $.data(this, widgetName);
|
||||
|
||||
// constructor
|
||||
if (!instance && !isMethodCall) {
|
||||
$.data(this, widgetName, new $.achtung(this))._init(args);
|
||||
}
|
||||
|
||||
if (!!instance && isMethodCall && $.isFunction(instance[options])) {
|
||||
instance[options].apply(instance, args);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.achtung = function (element) {
|
||||
if (!element || !element.nodeType) {
|
||||
var el = $('<div>');
|
||||
return el.achtung.apply(el, arguments);
|
||||
}
|
||||
|
||||
this.container = $(element);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Static members
|
||||
**/
|
||||
$.extend($.achtung, {
|
||||
version: '%%VERSION%%',
|
||||
overlay: false,
|
||||
wrapper: false,
|
||||
defaults: {
|
||||
timeout: 10,
|
||||
disableClose: false,
|
||||
icon: false,
|
||||
className: 'achtung-default',
|
||||
crossFadeMessage: 500, // 0 to disable
|
||||
animateClassSwitch: 0, // 0 to disable (doesn't work with gradient backgrounds)
|
||||
showEffects: {'opacity':'toggle'}, // ,'height':'toggle'},
|
||||
hideEffects: {'opacity':'toggle'}, // ,'height':'toggle'},
|
||||
showEffectDuration: 300,
|
||||
hideEffectDuration: 500
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Non-static members
|
||||
**/
|
||||
$.extend($.achtung.prototype, {
|
||||
container: false,
|
||||
icon: false,
|
||||
message: false,
|
||||
closeTimer: false,
|
||||
options: {},
|
||||
|
||||
_init: function (args) {
|
||||
var o, self = this;
|
||||
|
||||
o = this.options = $.extend.apply($, [{}, $.achtung.defaults].concat(args));
|
||||
|
||||
if ((o.animateClassSwitch > 0) && !('switchClass' in $.fn)) {
|
||||
o.animateClassSwitch = this.options.animateClassSwitch = 0;
|
||||
}
|
||||
|
||||
if (!o.disableClose) {
|
||||
$('<span class="achtung-close-button ui-icon ui-icon-close" />')
|
||||
.prependTo(this.container)
|
||||
.bind({
|
||||
click: function () { self.close(); }
|
||||
});
|
||||
}
|
||||
|
||||
this.changeIcon(o.icon, true);
|
||||
|
||||
if (o.message) {
|
||||
this.message = $('<span>', {
|
||||
'class': 'achtung-message',
|
||||
html: o.message
|
||||
}).appendTo(this.container);
|
||||
}
|
||||
|
||||
if ('className' in o) {
|
||||
this.container.addClass(o.className);
|
||||
}
|
||||
|
||||
if ('css' in o) {
|
||||
this.container.css(o.css);
|
||||
}
|
||||
|
||||
if (!$.achtung.overlay) {
|
||||
$.achtung.overlay = $('<div id="achtung-overlay"><div id="achtung-wrapper"></div></div>');
|
||||
$.achtung.overlay.appendTo(document.body);
|
||||
$.achtung.wrapper = $('#achtung-wrapper');
|
||||
}
|
||||
|
||||
this.container.addClass('achtung').hide().appendTo($.achtung.wrapper);
|
||||
|
||||
if (o.showEffects) {
|
||||
this.container.animate(o.showEffects, o.showEffectDuration);
|
||||
} else {
|
||||
this.container.show();
|
||||
}
|
||||
|
||||
this.timeout(o.timeout);
|
||||
},
|
||||
|
||||
timeout: function (timeout) {
|
||||
var self = this;
|
||||
|
||||
if (this.closeTimer) {
|
||||
clearTimeout(this.closeTimer);
|
||||
}
|
||||
|
||||
if (timeout > 0) {
|
||||
this.closeTimer = setTimeout(function () { self.close(); }, timeout * 1000);
|
||||
this.options.timeout = timeout;
|
||||
} else if (timeout < 0) {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the CSS class associated with this message.
|
||||
*
|
||||
* @param newClass string Name of new class to associate
|
||||
*/
|
||||
changeClass: function (newClass) {
|
||||
var oldClass = '' + this.options.className,
|
||||
self = this;
|
||||
|
||||
if (oldClass === newClass) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.container.queue(function (next) {
|
||||
if (self.options.animateClassSwitch > 0) {
|
||||
$(this).switchClass(oldClass, newClass, self.options.animateClassSwitch);
|
||||
} else {
|
||||
$(this).removeClass(oldClass).addClass(newClass);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
this.options.className = newClass;
|
||||
},
|
||||
|
||||
changeIcon: function (newIcon, force) {
|
||||
if (!force && this.options.icon === newIcon) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!!this.icon) {
|
||||
if (newIcon) {
|
||||
this.icon.removeClass(this.options.icon).addClass(newIcon);
|
||||
} else {
|
||||
this.icon.remove();
|
||||
this.icon = false;
|
||||
}
|
||||
} else if (newIcon) {
|
||||
this.icon = $('<span class="achtung-message-icon ui-icon ' + newIcon + '" />');
|
||||
this.container.prepend(this.icon);
|
||||
}
|
||||
|
||||
this.options.icon = newIcon;
|
||||
},
|
||||
|
||||
changeMessage: function (newMessage) {
|
||||
if (this.options.crossFadeMessage > 0) {
|
||||
this.message.clone()
|
||||
.css('position', 'absolute')
|
||||
.insertBefore(this.message)
|
||||
.fadeOut(this.options.crossFadeMessage, function () { $(this).remove(); });
|
||||
|
||||
this.message.hide().html(newMessage).fadeIn(this.options.crossFadeMessage);
|
||||
} else {
|
||||
this.message.html(newMessage);
|
||||
}
|
||||
|
||||
this.options.message = newMessage;
|
||||
},
|
||||
|
||||
update: function () {
|
||||
var options = $.extend.apply($, [{}].concat(Array.prototype.slice.call(arguments, 0))),
|
||||
map = {
|
||||
className: 'changeClass',
|
||||
css: 'css',
|
||||
icon: 'changeIcon',
|
||||
message: 'changeMessage',
|
||||
timeout: 'timeout'
|
||||
};
|
||||
|
||||
for (var prop in map) {
|
||||
if (prop in options) {
|
||||
this[map[prop]](options[prop]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
isVisible: function () {
|
||||
return (true === this.container.is(':visible'));
|
||||
},
|
||||
|
||||
_trigger: function (type, data) {
|
||||
this.container.trigger(widgetName + type, data);
|
||||
},
|
||||
|
||||
close: function () {
|
||||
var o = this.options, self = this;
|
||||
|
||||
this._trigger('close');
|
||||
|
||||
if (o.hideEffects) {
|
||||
this.container.animate(o.hideEffects, o.hideEffectDuration, function () {
|
||||
self.remove();
|
||||
});
|
||||
} else {
|
||||
this.container.hide();
|
||||
this.remove();
|
||||
}
|
||||
},
|
||||
|
||||
remove: function () {
|
||||
this.container.remove();
|
||||
|
||||
if ($.achtung.wrapper && !($.achtung.wrapper.contents().length)) {
|
||||
$.achtung.wrapper = false;
|
||||
$.achtung.overlay.remove();
|
||||
$.achtung.overlay = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,736 @@
|
||||
/*
|
||||
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__" ?
|
||||
// TODO: t-set + t-value + children node == scoped variable ?
|
||||
|
||||
(function() {
|
||||
|
||||
var QWeb2 = {
|
||||
expressions_cache: {},
|
||||
RESERVED_WORDS: 'true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,typeof,eval,void,Math,RegExp,Array,Object,Date'.split(','),
|
||||
ACTIONS_PRECEDENCE: 'foreach,if,call,set,esc,escf,raw,rawf,js,debug,log'.split(','),
|
||||
WORD_REPLACEMENT: {
|
||||
'and': '&&',
|
||||
'or': '||',
|
||||
'gt': '>',
|
||||
'gte': '>=',
|
||||
'lt': '<',
|
||||
'lte': '<='
|
||||
},
|
||||
tools: {
|
||||
exception: function(message, context) {
|
||||
context = context || {};
|
||||
var prefix = 'QWeb2';
|
||||
if (context.template) {
|
||||
prefix += " - template['" + context.template + "']";
|
||||
}
|
||||
throw new Error(prefix + ": " + message);
|
||||
},
|
||||
warning : function(message) {
|
||||
if (typeof(window) !== 'undefined' && window.console) {
|
||||
window.console.warn(message);
|
||||
}
|
||||
},
|
||||
trim: function(s, mode) {
|
||||
switch (mode) {
|
||||
case "left":
|
||||
return s.replace(/^\s*/, "");
|
||||
case "right":
|
||||
return s.replace(/\s*$/, "");
|
||||
default:
|
||||
return s.replace(/^\s*|\s*$/g, "");
|
||||
}
|
||||
},
|
||||
js_escape: function(s, noquotes) {
|
||||
return (noquotes ? '' : "'") + s.replace(/\r?\n/g, "\\n").replace(/'/g, "\\'") + (noquotes ? '' : "'");
|
||||
},
|
||||
html_escape: function(s, attribute) {
|
||||
if (s == null) {
|
||||
return '';
|
||||
}
|
||||
s = String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
if (attribute) {
|
||||
s = s.replace(/"/g, '"');
|
||||
}
|
||||
return s;
|
||||
},
|
||||
gen_attribute: function(o) {
|
||||
if (o !== null && o !== undefined) {
|
||||
if (o.constructor === Array) {
|
||||
if (o[1] !== null && o[1] !== undefined) {
|
||||
return this.format_attribute(o[0], o[1]);
|
||||
}
|
||||
} else if (typeof o === 'object') {
|
||||
var r = '';
|
||||
for (var k in o) {
|
||||
if (o.hasOwnProperty(k)) {
|
||||
r += this.gen_attribute([k, o[k]]);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
},
|
||||
format_attribute: function(name, value) {
|
||||
return ' ' + name + '="' + this.html_escape(value, true) + '"';
|
||||
},
|
||||
extend: function(dst, src, exclude) {
|
||||
for (var p in src) {
|
||||
if (src.hasOwnProperty(p) && !(exclude && this.arrayIndexOf(exclude, p) !== -1)) {
|
||||
dst[p] = src[p];
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
},
|
||||
arrayIndexOf : function(array, item) {
|
||||
for (var i = 0, ilen = array.length; i < ilen; i++) {
|
||||
if (array[i] === item) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
xml_node_to_string : function(node, childs_only) {
|
||||
if (childs_only) {
|
||||
var childs = node.childNodes, r = [];
|
||||
for (var i = 0, ilen = childs.length; i < ilen; i++) {
|
||||
r.push(this.xml_node_to_string(childs[i]));
|
||||
}
|
||||
return r.join('');
|
||||
} else {
|
||||
if (typeof XMLSerializer !== 'undefined') {
|
||||
return (new XMLSerializer()).serializeToString(node);
|
||||
} else {
|
||||
switch(node.nodeType) {
|
||||
case 1: return node.outerHTML;
|
||||
case 3: return node.data;
|
||||
case 4: return '<![CDATA[' + node.data + ']]>';
|
||||
case 8: return '<!-- ' + node.data + '-->';
|
||||
}
|
||||
throw new Error('Unknown node type ' + node.nodeType);
|
||||
}
|
||||
}
|
||||
},
|
||||
call: function(context, template, old_dict, _import, callback) {
|
||||
var new_dict = this.extend({}, old_dict);
|
||||
new_dict['__caller__'] = old_dict['__template__'];
|
||||
if (callback) {
|
||||
new_dict['__content__'] = callback(context, new_dict);
|
||||
}
|
||||
var r = context.engine._render(template, new_dict);
|
||||
if (_import) {
|
||||
if (_import === '*') {
|
||||
this.extend(old_dict, new_dict, ['__caller__', '__template__']);
|
||||
} else {
|
||||
_import = _import.split(',');
|
||||
for (var i = 0, ilen = _import.length; i < ilen; i++) {
|
||||
var v = _import[i];
|
||||
old_dict[v] = new_dict[v];
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
},
|
||||
foreach: function(context, enu, as, old_dict, callback) {
|
||||
if (enu != null) {
|
||||
var size, new_dict = this.extend({}, old_dict);
|
||||
new_dict[as + "_all"] = enu;
|
||||
var as_value = as + "_value",
|
||||
as_index = as + "_index",
|
||||
as_first = as + "_first",
|
||||
as_last = as + "_last",
|
||||
as_parity = as + "_parity";
|
||||
if (size = enu.length) {
|
||||
new_dict[as + "_size"] = size;
|
||||
for (var j = 0, jlen = enu.length; j < jlen; j++) {
|
||||
var cur = enu[j];
|
||||
new_dict[as_value] = cur;
|
||||
new_dict[as_index] = j;
|
||||
new_dict[as_first] = j === 0;
|
||||
new_dict[as_last] = j + 1 === size;
|
||||
new_dict[as_parity] = (j % 2 == 1 ? 'odd' : 'even');
|
||||
if (cur.constructor === Object) {
|
||||
this.extend(new_dict, cur);
|
||||
}
|
||||
new_dict[as] = cur;
|
||||
callback(context, new_dict);
|
||||
}
|
||||
} else if (enu.constructor == Number) {
|
||||
var _enu = [];
|
||||
for (var i = 0; i < enu; i++) {
|
||||
_enu.push(i);
|
||||
}
|
||||
this.foreach(context, _enu, as, old_dict, callback);
|
||||
} else {
|
||||
var index = 0;
|
||||
for (var k in enu) {
|
||||
if (enu.hasOwnProperty(k)) {
|
||||
var v = enu[k];
|
||||
new_dict[as_value] = v;
|
||||
new_dict[as_index] = index;
|
||||
new_dict[as_first] = index === 0;
|
||||
new_dict[as_parity] = (j % 2 == 1 ? 'odd' : 'even');
|
||||
new_dict[as] = k;
|
||||
callback(context, new_dict);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.exception("No enumerator given to foreach", context);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
QWeb2.Engine = (function() {
|
||||
function Engine() {
|
||||
// TODO: handle prefix at template level : t-prefix="x", don't forget to lowercase it
|
||||
this.prefix = 't';
|
||||
this.debug = false;
|
||||
this.templates_resources = []; // TODO: implement this.reload()
|
||||
this.templates = {};
|
||||
this.compiled_templates = {};
|
||||
this.extend_templates = {};
|
||||
this.default_dict = {};
|
||||
this.tools = QWeb2.tools;
|
||||
this.jQuery = window.jQuery;
|
||||
this.reserved_words = QWeb2.RESERVED_WORDS.slice(0);
|
||||
this.actions_precedence = QWeb2.ACTIONS_PRECEDENCE.slice(0);
|
||||
this.word_replacement = QWeb2.tools.extend({}, QWeb2.WORD_REPLACEMENT);
|
||||
this.preprocess_node = null;
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
this.add_template(arguments[i]);
|
||||
}
|
||||
}
|
||||
|
||||
QWeb2.tools.extend(Engine.prototype, {
|
||||
add_template : function(template) {
|
||||
this.templates_resources.push(template);
|
||||
if (template.constructor === String) {
|
||||
template = this.load_xml(template);
|
||||
}
|
||||
var ec = (template.documentElement && template.documentElement.childNodes) || template.childNodes || [];
|
||||
for (var i = 0; i < ec.length; i++) {
|
||||
var node = ec[i];
|
||||
if (node.nodeType === 1) {
|
||||
if (node.nodeName == 'parsererror') {
|
||||
return this.tools.exception(node.innerText);
|
||||
}
|
||||
var name = node.getAttribute(this.prefix + '-name');
|
||||
var extend = node.getAttribute(this.prefix + '-extend');
|
||||
if (name && extend) {
|
||||
// Clone template and extend it
|
||||
if (!this.templates[extend]) {
|
||||
return this.tools.exception("Can't clone undefined template " + extend);
|
||||
}
|
||||
this.templates[name] = this.templates[extend].cloneNode(true);
|
||||
extend = name;
|
||||
name = undefined;
|
||||
}
|
||||
if (name) {
|
||||
this.templates[name] = node;
|
||||
this.compiled_templates[name] = null;
|
||||
} else if (extend) {
|
||||
delete(this.compiled_templates[extend]);
|
||||
if (this.extend_templates[extend]) {
|
||||
this.extend_templates[extend].push(node);
|
||||
} else {
|
||||
this.extend_templates[extend] = [node];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
load_xml : function(s) {
|
||||
s = this.tools.trim(s);
|
||||
if (s.charAt(0) === '<') {
|
||||
return this.load_xml_string(s);
|
||||
} else {
|
||||
var req = this.get_xhr();
|
||||
if (req) {
|
||||
// TODO: third parameter is async : https://developer.mozilla.org/en/XMLHttpRequest#open()
|
||||
// do an on_ready in QWeb2{} that could be passed to add_template
|
||||
if (this.debug) {
|
||||
s += '?debug=' + (new Date()).getTime(); // TODO fme: do it properly in case there's already url parameters
|
||||
}
|
||||
req.open('GET', s, false);
|
||||
req.send(null);
|
||||
var xDoc = req.responseXML;
|
||||
if (xDoc) {
|
||||
if (!xDoc.documentElement) {
|
||||
throw new Error("QWeb2: This xml document has no root document : " + xDoc.responseText);
|
||||
}
|
||||
if (xDoc.documentElement.nodeName == "parsererror") {
|
||||
return this.tools.exception(xDoc.documentElement.childNodes[0].nodeValue);
|
||||
}
|
||||
return xDoc;
|
||||
} else {
|
||||
return this.load_xml_string(req.responseText);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
load_xml_string : function(s) {
|
||||
if (window.DOMParser) {
|
||||
var dp = new DOMParser();
|
||||
var r = dp.parseFromString(s, "text/xml");
|
||||
if (r.body && r.body.firstChild && r.body.firstChild.nodeName == 'parsererror') {
|
||||
return this.tools.exception(r.body.innerText);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
var xDoc;
|
||||
try {
|
||||
// new ActiveXObject("Msxml2.DOMDocument.4.0");
|
||||
xDoc = new ActiveXObject("MSXML2.DOMDocument");
|
||||
} catch (e) {
|
||||
return this.tools.exception(
|
||||
"Could not find a DOM Parser: " + e.message);
|
||||
}
|
||||
xDoc.async = false;
|
||||
xDoc.preserveWhiteSpace = true;
|
||||
xDoc.loadXML(s);
|
||||
return xDoc;
|
||||
},
|
||||
has_template : function(template) {
|
||||
return !!this.templates[template];
|
||||
},
|
||||
get_xhr : function() {
|
||||
if (window.XMLHttpRequest) {
|
||||
return new window.XMLHttpRequest();
|
||||
}
|
||||
try {
|
||||
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
compile : function(node) {
|
||||
var e = new QWeb2.Element(this, node);
|
||||
var template = node.getAttribute(this.prefix + '-name');
|
||||
return " /* 'this' refers to Qweb2.Engine instance */\n" +
|
||||
" var context = { engine : this, template : " + (this.tools.js_escape(template)) + " };\n" +
|
||||
" dict = dict || {};\n" +
|
||||
" dict['__template__'] = '" + template + "';\n" +
|
||||
" var r = [];\n" +
|
||||
" /* START TEMPLATE */" +
|
||||
(this.debug ? "" : " try {\n") +
|
||||
(e.compile()) + "\n" +
|
||||
" /* END OF TEMPLATE */" +
|
||||
(this.debug ? "" : " } catch(error) {\n" +
|
||||
" if (console && console.exception) console.exception(error);\n" +
|
||||
" context.engine.tools.exception('Runtime Error: ' + error, context);\n") +
|
||||
(this.debug ? "" : " }\n") +
|
||||
" return r.join('');";
|
||||
},
|
||||
render : function(template, dict) {
|
||||
dict = dict || {};
|
||||
QWeb2.tools.extend(dict, this.default_dict);
|
||||
/*if (this.debug && window['console'] !== undefined) {
|
||||
console.time("QWeb render template " + template);
|
||||
}*/
|
||||
var r = this._render(template, dict);
|
||||
/*if (this.debug && window['console'] !== undefined) {
|
||||
console.timeEnd("QWeb render template " + template);
|
||||
}*/
|
||||
return r;
|
||||
},
|
||||
_render : function(template, dict) {
|
||||
if (this.compiled_templates[template]) {
|
||||
return this.compiled_templates[template].apply(this, [dict || {}]);
|
||||
} else if (this.templates[template]) {
|
||||
var ext;
|
||||
if (ext = this.extend_templates[template]) {
|
||||
var extend_node;
|
||||
while (extend_node = ext.shift()) {
|
||||
this.extend(template, extend_node);
|
||||
}
|
||||
}
|
||||
var code = this.compile(this.templates[template]), tcompiled;
|
||||
try {
|
||||
tcompiled = new Function(['dict'], code);
|
||||
} catch (error) {
|
||||
if (this.debug && window.console) {
|
||||
console.log(code);
|
||||
}
|
||||
this.tools.exception("Error evaluating template: " + error, { template: name });
|
||||
}
|
||||
if (!tcompiled) {
|
||||
this.tools.exception("Error evaluating template: (IE?)" + error, { template: name });
|
||||
}
|
||||
this.compiled_templates[template] = tcompiled;
|
||||
return this.render(template, dict);
|
||||
} else {
|
||||
return this.tools.exception("Template '" + template + "' not found");
|
||||
}
|
||||
},
|
||||
extend : function(template, extend_node) {
|
||||
if (!this.jQuery) {
|
||||
return this.tools.exception("Can't extend template " + template + " without jQuery");
|
||||
}
|
||||
var template_dest = this.templates[template];
|
||||
for (var i = 0, ilen = extend_node.childNodes.length; i < ilen; i++) {
|
||||
var child = extend_node.childNodes[i];
|
||||
if (child.nodeType === 1) {
|
||||
var jquery = child.getAttribute(this.prefix + '-jquery'),
|
||||
operation = child.getAttribute(this.prefix + '-operation'),
|
||||
target,
|
||||
error_msg = "Error while extending template '" + template;
|
||||
if (jquery) {
|
||||
target = this.jQuery(jquery, template_dest);
|
||||
} else {
|
||||
this.tools.exception(error_msg + "No expression given");
|
||||
}
|
||||
error_msg += "' (expression='" + jquery + "') : ";
|
||||
if (operation) {
|
||||
var allowed_operations = "append,prepend,before,after,replace,inner".split(',');
|
||||
if (this.tools.arrayIndexOf(allowed_operations, operation) == -1) {
|
||||
this.tools.exception(error_msg + "Invalid operation : '" + operation + "'");
|
||||
}
|
||||
operation = {'replace' : 'replaceWith', 'inner' : 'html'}[operation] || operation;
|
||||
target[operation](child.cloneNode(true).childNodes);
|
||||
} else {
|
||||
try {
|
||||
var f = new Function(['$', 'document'], this.tools.xml_node_to_string(child, true));
|
||||
} catch(error) {
|
||||
return this.tools.exception("Parse " + error_msg + error);
|
||||
}
|
||||
try {
|
||||
f.apply(target, [this.jQuery, template_dest.ownerDocument]);
|
||||
} catch(error) {
|
||||
return this.tools.exception("Runtime " + error_msg + error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return Engine;
|
||||
})();
|
||||
|
||||
QWeb2.Element = (function() {
|
||||
function Element(engine, node) {
|
||||
this.engine = engine;
|
||||
this.node = node;
|
||||
this.tag = node.tagName;
|
||||
this.actions = {};
|
||||
this.actions_done = [];
|
||||
this.attributes = {};
|
||||
this.children = [];
|
||||
this._top = [];
|
||||
this._bottom = [];
|
||||
this._indent = 1;
|
||||
this.process_children = true;
|
||||
var childs = this.node.childNodes;
|
||||
if (childs) {
|
||||
for (var i = 0, ilen = childs.length; i < ilen; i++) {
|
||||
this.children.push(new QWeb2.Element(this.engine, childs[i]));
|
||||
}
|
||||
}
|
||||
var attrs = this.node.attributes;
|
||||
if (attrs) {
|
||||
for (var j = 0, jlen = attrs.length; j < jlen; j++) {
|
||||
var attr = attrs[j];
|
||||
var name = attr.name;
|
||||
var m = name.match(new RegExp("^" + this.engine.prefix + "-(.+)"));
|
||||
if (m) {
|
||||
name = m[1];
|
||||
if (name === 'name') {
|
||||
continue;
|
||||
}
|
||||
this.actions[name] = attr.value;
|
||||
} else {
|
||||
this.attributes[name] = attr.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.engine.preprocess_node) {
|
||||
this.engine.preprocess_node.call(this);
|
||||
}
|
||||
}
|
||||
|
||||
QWeb2.tools.extend(Element.prototype, {
|
||||
compile : function() {
|
||||
var r = [],
|
||||
instring = false,
|
||||
lines = this._compile().split('\n');
|
||||
for (var i = 0, ilen = lines.length; i < ilen; i++) {
|
||||
var m, line = lines[i];
|
||||
if (m = line.match(/^(\s*)\/\/@string=(.*)/)) {
|
||||
if (instring) {
|
||||
if (this.engine.debug) {
|
||||
// Split string lines in indented r.push arguments
|
||||
r.push((m[2].indexOf("\\n") != -1 ? "',\n\t" + m[1] + "'" : '') + m[2]);
|
||||
} else {
|
||||
r.push(m[2]);
|
||||
}
|
||||
} else {
|
||||
r.push(m[1] + "r.push('" + m[2]);
|
||||
instring = true;
|
||||
}
|
||||
} else {
|
||||
if (instring) {
|
||||
r.push("');\n");
|
||||
}
|
||||
instring = false;
|
||||
r.push(line + '\n');
|
||||
}
|
||||
}
|
||||
return r.join('');
|
||||
},
|
||||
_compile : function() {
|
||||
switch (this.node.nodeType) {
|
||||
case 3:
|
||||
case 4:
|
||||
this.top_string(this.node.data);
|
||||
break;
|
||||
case 1:
|
||||
this.compile_element();
|
||||
}
|
||||
var r = this._top.join('');
|
||||
if (this.process_children) {
|
||||
for (var i = 0, ilen = this.children.length; i < ilen; i++) {
|
||||
var child = this.children[i];
|
||||
child._indent = this._indent;
|
||||
r += child._compile();
|
||||
}
|
||||
}
|
||||
r += this._bottom.join('');
|
||||
return r;
|
||||
},
|
||||
format_expression : function(e) {
|
||||
/* Naive format expression builder. Replace reserved words and variables to dict[variable]
|
||||
* Does not handle spaces before dot yet, and causes problems for anonymous functions. Use t-js="" for that */
|
||||
if (QWeb2.expressions_cache[e]) {
|
||||
return QWeb2.expressions_cache[e];
|
||||
}
|
||||
var chars = e.split(''),
|
||||
instring = '',
|
||||
invar = '',
|
||||
invar_pos = 0,
|
||||
r = '';
|
||||
chars.push(' ');
|
||||
for (var i = 0, ilen = chars.length; i < ilen; i++) {
|
||||
var c = chars[i];
|
||||
if (instring.length) {
|
||||
if (c === instring && chars[i - 1] !== "\\") {
|
||||
instring = '';
|
||||
}
|
||||
} else if (c === '"' || c === "'") {
|
||||
instring = c;
|
||||
} else if (c.match(/[a-zA-Z_\$]/) && !invar.length) {
|
||||
invar = c;
|
||||
invar_pos = i;
|
||||
continue;
|
||||
} else if (c.match(/\W/) && invar.length) {
|
||||
// TODO: Should check for possible spaces before dot
|
||||
if (chars[invar_pos - 1] !== '.' && QWeb2.tools.arrayIndexOf(this.engine.reserved_words, invar) < 0) {
|
||||
invar = this.engine.word_replacement[invar] || ("dict['" + invar + "']");
|
||||
}
|
||||
r += invar;
|
||||
invar = '';
|
||||
} else if (invar.length) {
|
||||
invar += c;
|
||||
continue;
|
||||
}
|
||||
r += c;
|
||||
}
|
||||
r = r.slice(0, -1);
|
||||
QWeb2.expressions_cache[e] = r;
|
||||
return r;
|
||||
},
|
||||
string_interpolation : function(s) {
|
||||
if (!s) {
|
||||
return "''";
|
||||
}
|
||||
var regex = /^{(.*)}(.*)/,
|
||||
src = s.split(/#/),
|
||||
r = [];
|
||||
for (var i = 0, ilen = src.length; i < ilen; i++) {
|
||||
var val = src[i],
|
||||
m = val.match(regex);
|
||||
if (m) {
|
||||
r.push("(" + this.format_expression(m[1]) + ")");
|
||||
if (m[2]) {
|
||||
r.push(this.engine.tools.js_escape(m[2]));
|
||||
}
|
||||
} else if (!(i === 0 && val === '')) {
|
||||
r.push(this.engine.tools.js_escape((i === 0 ? '' : '#') + val));
|
||||
}
|
||||
}
|
||||
return r.join(' + ');
|
||||
},
|
||||
indent : function() {
|
||||
return this._indent++;
|
||||
},
|
||||
dedent : function() {
|
||||
if (this._indent !== 0) {
|
||||
return this._indent--;
|
||||
}
|
||||
},
|
||||
get_indent : function() {
|
||||
return new Array(this._indent + 1).join("\t");
|
||||
},
|
||||
top : function(s) {
|
||||
return this._top.push(this.get_indent() + s + '\n');
|
||||
},
|
||||
top_string : function(s) {
|
||||
return this._top.push(this.get_indent() + "//@string=" + this.engine.tools.js_escape(s, true) + '\n');
|
||||
},
|
||||
bottom : function(s) {
|
||||
return this._bottom.unshift(this.get_indent() + s + '\n');
|
||||
},
|
||||
bottom_string : function(s) {
|
||||
return this._bottom.unshift(this.get_indent() + "//@string=" + this.engine.tools.js_escape(s, true) + '\n');
|
||||
},
|
||||
compile_element : function() {
|
||||
for (var i = 0, ilen = this.engine.actions_precedence.length; i < ilen; i++) {
|
||||
var a = this.engine.actions_precedence[i];
|
||||
if (a in this.actions) {
|
||||
var value = this.actions[a];
|
||||
var key = 'compile_action_' + a;
|
||||
if (this[key]) {
|
||||
this[key](value);
|
||||
} else if (this.engine[key]) {
|
||||
this.engine[key].call(this, value);
|
||||
} else {
|
||||
this.engine.tools.exception("No handler method for action '" + a + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.tag.toLowerCase() !== this.engine.prefix) {
|
||||
var tag = "<" + this.tag;
|
||||
for (var a in this.attributes) {
|
||||
tag += this.engine.tools.gen_attribute([a, this.attributes[a]]);
|
||||
}
|
||||
this.top_string(tag);
|
||||
if (this.actions.att) {
|
||||
this.top("r.push(context.engine.tools.gen_attribute(" + (this.format_expression(this.actions.att)) + "));");
|
||||
}
|
||||
for (var a in this.actions) {
|
||||
var v = this.actions[a];
|
||||
var m = a.match(/att-(.+)/);
|
||||
if (m) {
|
||||
this.top("r.push(context.engine.tools.gen_attribute(['" + m[1] + "', (" + (this.format_expression(v)) + ")]));");
|
||||
}
|
||||
var m = a.match(/attf-(.+)/);
|
||||
if (m) {
|
||||
this.top("r.push(context.engine.tools.gen_attribute(['" + m[1] + "', (" + (this.string_interpolation(v)) + ")]));");
|
||||
}
|
||||
}
|
||||
if (this.children.length || this.actions.opentag === 'true') {
|
||||
this.top_string(">");
|
||||
this.bottom_string("</" + this.tag + ">");
|
||||
} else {
|
||||
this.top_string("/>");
|
||||
}
|
||||
}
|
||||
},
|
||||
compile_action_if : function(value) {
|
||||
this.top("if (" + (this.format_expression(value)) + ") {");
|
||||
this.bottom("}");
|
||||
this.indent();
|
||||
},
|
||||
compile_action_foreach : function(value) {
|
||||
var as = this.actions['as'] || value.replace(/[^a-zA-Z0-9]/g, '_');
|
||||
//TODO: exception if t-as not valid
|
||||
this.top("context.engine.tools.foreach(context, " + (this.format_expression(value)) + ", " + (this.engine.tools.js_escape(as)) + ", dict, function(context, dict) {");
|
||||
this.bottom("});");
|
||||
this.indent();
|
||||
},
|
||||
compile_action_call : function(value) {
|
||||
var _import = this.actions['import'] || '';
|
||||
if (this.children.length === 0) {
|
||||
return this.top("r.push(context.engine.tools.call(context, " + (this.engine.tools.js_escape(value)) + ", dict, " + (this.engine.tools.js_escape(_import)) + "));");
|
||||
} else {
|
||||
this.top("r.push(context.engine.tools.call(context, " + (this.engine.tools.js_escape(value)) + ", dict, " + (this.engine.tools.js_escape(_import)) + ", function(context, dict) {");
|
||||
this.bottom("}));");
|
||||
this.indent();
|
||||
this.top("var r = [];");
|
||||
return this.bottom("return r.join('');");
|
||||
}
|
||||
},
|
||||
compile_action_set : function(value) {
|
||||
var variable = this.format_expression(value);
|
||||
if (this.actions['value']) {
|
||||
if (this.children.length) {
|
||||
this.engine.tools.warning("@set with @value plus node chidren found. Children are ignored.");
|
||||
}
|
||||
this.top(variable + " = (" + (this.format_expression(this.actions['value'])) + ");");
|
||||
this.process_children = false;
|
||||
} else {
|
||||
if (this.children.length === 0) {
|
||||
this.top(variable + " = '';");
|
||||
} else if (this.children.length === 1 && this.children[0].node.nodeType === 3) {
|
||||
this.top(variable + " = " + (this.engine.tools.js_escape(this.children[0].node.data)) + ";");
|
||||
this.process_children = false;
|
||||
} else {
|
||||
this.top(variable + " = (function(dict) {");
|
||||
this.bottom("})(dict);");
|
||||
this.indent();
|
||||
this.top("var r = [];");
|
||||
this.bottom("return r.join('');");
|
||||
}
|
||||
}
|
||||
},
|
||||
compile_action_esc : function(value) {
|
||||
this.top("r.push(context.engine.tools.html_escape(" + (this.format_expression(value)) + "));");
|
||||
},
|
||||
compile_action_escf : function(value) {
|
||||
this.top("r.push(context.engine.tools.html_escape(" + (this.string_interpolation(value)) + "));");
|
||||
},
|
||||
compile_action_raw : function(value) {
|
||||
this.top("r.push(" + (this.format_expression(value)) + ");");
|
||||
},
|
||||
compile_action_rawf : function(value) {
|
||||
this.top("r.push(" + (this.string_interpolation(value)) + ");");
|
||||
},
|
||||
compile_action_js : function(value) {
|
||||
this.top("(function(" + value + ") {");
|
||||
this.bottom("})(dict);");
|
||||
this.indent();
|
||||
var lines = this.engine.tools.xml_node_to_string(this.node, true).split(/\r?\n/);
|
||||
for (var i = 0, ilen = lines.length; i < ilen; i++) {
|
||||
this.top(lines[i]);
|
||||
}
|
||||
this.process_children = false;
|
||||
},
|
||||
compile_action_debug : function(value) {
|
||||
this.top("debugger;");
|
||||
},
|
||||
compile_action_log : function(value) {
|
||||
this.top("console.log(" + this.format_expression(value) + ");");
|
||||
}
|
||||
});
|
||||
return Element;
|
||||
})();
|
||||
|
||||
window.QWeb2 = QWeb2;
|
||||
|
||||
})();
|
||||
+119
-59
@@ -1,18 +1,18 @@
|
||||
/** vim: et:ts=4:sw=4:sts=4
|
||||
* @license RequireJS 2.1.2 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
|
||||
* @license RequireJS 2.1.8 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
|
||||
* Available via the MIT or new BSD license.
|
||||
* see: http://github.com/jrburke/requirejs for details
|
||||
*/
|
||||
//Not using strict: uneven strict support in browsers, #392, and causes
|
||||
//problems with requirejs.exec()/transpiler plugins that may not be strict.
|
||||
/*jslint regexp: true, nomen: true, sloppy: true */
|
||||
/*global window, navigator, document, importScripts, jQuery, setTimeout, opera */
|
||||
/*global window, navigator, document, importScripts, setTimeout, opera */
|
||||
|
||||
var requirejs, require, define;
|
||||
(function (global) {
|
||||
var req, s, head, baseElement, dataMain, src,
|
||||
interactiveScript, currentlyAddingScript, mainScript, subPath,
|
||||
version = '2.1.2',
|
||||
version = '2.1.8',
|
||||
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
|
||||
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
|
||||
jsSuffixRegExp = /\.js$/,
|
||||
@@ -21,9 +21,8 @@ var requirejs, require, define;
|
||||
ostring = op.toString,
|
||||
hasOwn = op.hasOwnProperty,
|
||||
ap = Array.prototype,
|
||||
aps = ap.slice,
|
||||
apsp = ap.splice,
|
||||
isBrowser = !!(typeof window !== 'undefined' && navigator && document),
|
||||
isBrowser = !!(typeof window !== 'undefined' && navigator && window.document),
|
||||
isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
|
||||
//PS3 indicates loaded and complete, but need to wait for complete
|
||||
//specifically. Sequence is 'loading', 'loaded', execution,
|
||||
@@ -135,6 +134,10 @@ var requirejs, require, define;
|
||||
return document.getElementsByTagName('script');
|
||||
}
|
||||
|
||||
function defaultOnError(err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
//Allow getting a global that expressed in
|
||||
//dot notation, like 'a.b.c'.
|
||||
function getGlobal(value) {
|
||||
@@ -192,15 +195,21 @@ var requirejs, require, define;
|
||||
var inCheckLoaded, Module, context, handlers,
|
||||
checkLoadedTimeoutId,
|
||||
config = {
|
||||
//Defaults. Do not set a default for map
|
||||
//config to speed up normalize(), which
|
||||
//will run faster if there is no default.
|
||||
waitSeconds: 7,
|
||||
baseUrl: './',
|
||||
paths: {},
|
||||
pkgs: {},
|
||||
shim: {},
|
||||
map: {},
|
||||
config: {}
|
||||
},
|
||||
registry = {},
|
||||
//registry of just enabled modules, to speed
|
||||
//cycle breaking code when lots of modules
|
||||
//are registered, but not activated.
|
||||
enabledRegistry = {},
|
||||
undefEvents = {},
|
||||
defQueue = [],
|
||||
defined = {},
|
||||
@@ -296,7 +305,7 @@ var requirejs, require, define;
|
||||
}
|
||||
|
||||
//Apply map config if available.
|
||||
if (applyMap && (baseParts || starMap) && map) {
|
||||
if (applyMap && map && (baseParts || starMap)) {
|
||||
nameParts = name.split('/');
|
||||
|
||||
for (i = nameParts.length; i > 0; i -= 1) {
|
||||
@@ -495,7 +504,12 @@ var requirejs, require, define;
|
||||
fn(defined[id]);
|
||||
}
|
||||
} else {
|
||||
getModule(depMap).on(name, fn);
|
||||
mod = getModule(depMap);
|
||||
if (mod.error && name === 'error') {
|
||||
fn(mod.error);
|
||||
} else {
|
||||
mod.on(name, fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -566,7 +580,13 @@ var requirejs, require, define;
|
||||
id: mod.map.id,
|
||||
uri: mod.map.url,
|
||||
config: function () {
|
||||
return (config.config && getOwn(config.config, mod.map.id)) || {};
|
||||
var c,
|
||||
pkg = getOwn(config.pkgs, mod.map.id);
|
||||
// For packages, only support config targeted
|
||||
// at the main module.
|
||||
c = pkg ? getOwn(config.config, mod.map.id + '/' + pkg.main) :
|
||||
getOwn(config.config, mod.map.id);
|
||||
return c || {};
|
||||
},
|
||||
exports: defined[mod.map.id]
|
||||
});
|
||||
@@ -577,6 +597,7 @@ var requirejs, require, define;
|
||||
function cleanRegistry(id) {
|
||||
//Clean up machinery used for waiting modules.
|
||||
delete registry[id];
|
||||
delete enabledRegistry[id];
|
||||
}
|
||||
|
||||
function breakCycle(mod, traced, processed) {
|
||||
@@ -625,7 +646,7 @@ var requirejs, require, define;
|
||||
inCheckLoaded = true;
|
||||
|
||||
//Figure out the state of all the modules.
|
||||
eachProp(registry, function (mod) {
|
||||
eachProp(enabledRegistry, function (mod) {
|
||||
map = mod.map;
|
||||
modId = map.id;
|
||||
|
||||
@@ -806,7 +827,7 @@ var requirejs, require, define;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks is the module is ready to define itself, and if so,
|
||||
* Checks if the module is ready to define itself, and if so,
|
||||
* define it.
|
||||
*/
|
||||
check: function () {
|
||||
@@ -834,8 +855,13 @@ var requirejs, require, define;
|
||||
if (this.depCount < 1 && !this.defined) {
|
||||
if (isFunction(factory)) {
|
||||
//If there is an error listener, favor passing
|
||||
//to that instead of throwing an error.
|
||||
if (this.events.error) {
|
||||
//to that instead of throwing an error. However,
|
||||
//only do it for define()'d modules. require
|
||||
//errbacks should not be called for failures in
|
||||
//their callbacks (#699). However if a global
|
||||
//onError is set, use that.
|
||||
if ((this.events.error && this.map.isDefine) ||
|
||||
req.onError !== defaultOnError) {
|
||||
try {
|
||||
exports = context.execCb(id, factory, depExports, exports);
|
||||
} catch (e) {
|
||||
@@ -863,8 +889,8 @@ var requirejs, require, define;
|
||||
|
||||
if (err) {
|
||||
err.requireMap = this.map;
|
||||
err.requireModules = [this.map.id];
|
||||
err.requireType = 'define';
|
||||
err.requireModules = this.map.isDefine ? [this.map.id] : null;
|
||||
err.requireType = this.map.isDefine ? 'define' : 'require';
|
||||
return onError((this.error = err));
|
||||
}
|
||||
|
||||
@@ -884,7 +910,7 @@ var requirejs, require, define;
|
||||
}
|
||||
|
||||
//Clean up
|
||||
delete registry[id];
|
||||
cleanRegistry(id);
|
||||
|
||||
this.defined = true;
|
||||
}
|
||||
@@ -918,8 +944,7 @@ var requirejs, require, define;
|
||||
name = this.map.name,
|
||||
parentName = this.map.parentMap ? this.map.parentMap.name : null,
|
||||
localRequire = context.makeRequire(map.parentMap, {
|
||||
enableBuildCallback: true,
|
||||
skipMap: true
|
||||
enableBuildCallback: true
|
||||
});
|
||||
|
||||
//If current map is not normalized, wait for that
|
||||
@@ -1017,8 +1042,11 @@ var requirejs, require, define;
|
||||
try {
|
||||
req.exec(text);
|
||||
} catch (e) {
|
||||
throw new Error('fromText eval for ' + moduleName +
|
||||
' failed: ' + e);
|
||||
return onError(makeError('fromtexteval',
|
||||
'fromText eval for ' + id +
|
||||
' failed: ' + e,
|
||||
e,
|
||||
[id]));
|
||||
}
|
||||
|
||||
if (hasInteractive) {
|
||||
@@ -1048,6 +1076,7 @@ var requirejs, require, define;
|
||||
},
|
||||
|
||||
enable: function () {
|
||||
enabledRegistry[this.map.id] = this;
|
||||
this.enabled = true;
|
||||
|
||||
//Set flag mentioning that the module is enabling,
|
||||
@@ -1084,7 +1113,7 @@ var requirejs, require, define;
|
||||
}));
|
||||
|
||||
if (this.errback) {
|
||||
on(depMap, 'error', this.errback);
|
||||
on(depMap, 'error', bind(this, this.errback));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1207,6 +1236,7 @@ var requirejs, require, define;
|
||||
Module: Module,
|
||||
makeModuleMap: makeModuleMap,
|
||||
nextTick: req.nextTick,
|
||||
onError: onError,
|
||||
|
||||
/**
|
||||
* Set a configuration for the context.
|
||||
@@ -1233,6 +1263,9 @@ var requirejs, require, define;
|
||||
eachProp(cfg, function (value, prop) {
|
||||
if (objs[prop]) {
|
||||
if (prop === 'map') {
|
||||
if (!config.map) {
|
||||
config.map = {};
|
||||
}
|
||||
mixin(config[prop], value, true, true);
|
||||
} else {
|
||||
mixin(config[prop], value, true);
|
||||
@@ -1344,7 +1377,7 @@ var requirejs, require, define;
|
||||
//Synchronous access to one module. If require.get is
|
||||
//available (as in the Node adapter), prefer that.
|
||||
if (req.get) {
|
||||
return req.get(context, deps, relMap);
|
||||
return req.get(context, deps, relMap, localRequire);
|
||||
}
|
||||
|
||||
//Normalize module name, if it contains . or ..
|
||||
@@ -1395,16 +1428,20 @@ var requirejs, require, define;
|
||||
* plain URLs like nameToUrl.
|
||||
*/
|
||||
toUrl: function (moduleNamePlusExt) {
|
||||
var index = moduleNamePlusExt.lastIndexOf('.'),
|
||||
ext = null;
|
||||
var ext,
|
||||
index = moduleNamePlusExt.lastIndexOf('.'),
|
||||
segment = moduleNamePlusExt.split('/')[0],
|
||||
isRelative = segment === '.' || segment === '..';
|
||||
|
||||
if (index !== -1) {
|
||||
//Have a file extension alias, and it is not the
|
||||
//dots from a relative path.
|
||||
if (index !== -1 && (!isRelative || index > 1)) {
|
||||
ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
|
||||
moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
|
||||
}
|
||||
|
||||
return context.nameToUrl(normalize(moduleNamePlusExt,
|
||||
relMap && relMap.id, true), ext);
|
||||
relMap && relMap.id, true), ext, true);
|
||||
},
|
||||
|
||||
defined: function (id) {
|
||||
@@ -1449,10 +1486,11 @@ var requirejs, require, define;
|
||||
|
||||
/**
|
||||
* Called to enable a module if it is still in the registry
|
||||
* awaiting enablement. parent module is passed in for context,
|
||||
* used by the optimizer.
|
||||
* awaiting enablement. A second arg, parent, the parent module,
|
||||
* is passed in for context, when this method is overriden by
|
||||
* the optimizer. Not shown here to keep code compact.
|
||||
*/
|
||||
enable: function (depMap, parent) {
|
||||
enable: function (depMap) {
|
||||
var mod = getOwn(registry, depMap.id);
|
||||
if (mod) {
|
||||
getModule(depMap).enable();
|
||||
@@ -1522,7 +1560,7 @@ var requirejs, require, define;
|
||||
* it is assumed to have already been normalized. This is an
|
||||
* internal API, not a public one. Use toUrl for the public API.
|
||||
*/
|
||||
nameToUrl: function (moduleName, ext) {
|
||||
nameToUrl: function (moduleName, ext, skipExt) {
|
||||
var paths, pkgs, pkg, pkgPath, syms, i, parentModule, url,
|
||||
parentPath;
|
||||
|
||||
@@ -1571,7 +1609,7 @@ var requirejs, require, define;
|
||||
|
||||
//Join the path parts together, then figure out if baseUrl is needed.
|
||||
url = syms.join('/');
|
||||
url += (ext || (/\?/.test(url) ? '' : '.js'));
|
||||
url += (ext || (/\?/.test(url) || skipExt ? '' : '.js'));
|
||||
url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
|
||||
}
|
||||
|
||||
@@ -1587,7 +1625,7 @@ var requirejs, require, define;
|
||||
},
|
||||
|
||||
/**
|
||||
* Executes a module callack function. Broken out as a separate function
|
||||
* Executes a module callback function. Broken out as a separate function
|
||||
* solely to allow the build system to sequence the files in the built
|
||||
* layer in the right sequence.
|
||||
*
|
||||
@@ -1625,7 +1663,7 @@ var requirejs, require, define;
|
||||
onScriptError: function (evt) {
|
||||
var data = getScriptData(evt);
|
||||
if (!hasPathFallback(data.id)) {
|
||||
return onError(makeError('scripterror', 'Script error', evt, [data.id]));
|
||||
return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id]));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1754,8 +1792,19 @@ var requirejs, require, define;
|
||||
* function. Intercept/override it if you want custom error handling.
|
||||
* @param {Error} err the error object.
|
||||
*/
|
||||
req.onError = function (err) {
|
||||
throw err;
|
||||
req.onError = defaultOnError;
|
||||
|
||||
/**
|
||||
* Creates the node for the load command. Only used in browser envs.
|
||||
*/
|
||||
req.createNode = function (config, moduleName, url) {
|
||||
var node = config.xhtml ?
|
||||
document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
|
||||
document.createElement('script');
|
||||
node.type = config.scriptType || 'text/javascript';
|
||||
node.charset = 'utf-8';
|
||||
node.async = true;
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1772,12 +1821,7 @@ var requirejs, require, define;
|
||||
node;
|
||||
if (isBrowser) {
|
||||
//In the browser so use a script tag
|
||||
node = config.xhtml ?
|
||||
document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
|
||||
document.createElement('script');
|
||||
node.type = config.scriptType || 'text/javascript';
|
||||
node.charset = 'utf-8';
|
||||
node.async = true;
|
||||
node = req.createNode(config, moduleName, url);
|
||||
|
||||
node.setAttribute('data-requirecontext', context.contextName);
|
||||
node.setAttribute('data-requiremodule', moduleName);
|
||||
@@ -1810,7 +1854,7 @@ var requirejs, require, define;
|
||||
node.attachEvent('onreadystatechange', context.onScriptLoad);
|
||||
//It would be great to add an error handler here to catch
|
||||
//404s in IE9+. However, onreadystatechange will fire before
|
||||
//the error handler, so that does not help. If addEvenListener
|
||||
//the error handler, so that does not help. If addEventListener
|
||||
//is used, then IE will fire error before load, but we cannot
|
||||
//use that pathway given the connect.microsoft.com issue
|
||||
//mentioned above about not doing the 'script execute,
|
||||
@@ -1839,16 +1883,24 @@ var requirejs, require, define;
|
||||
|
||||
return node;
|
||||
} else if (isWebWorker) {
|
||||
//In a web worker, use importScripts. This is not a very
|
||||
//efficient use of importScripts, importScripts will block until
|
||||
//its script is downloaded and evaluated. However, if web workers
|
||||
//are in play, the expectation that a build has been done so that
|
||||
//only one script needs to be loaded anyway. This may need to be
|
||||
//reevaluated if other use cases become common.
|
||||
importScripts(url);
|
||||
try {
|
||||
//In a web worker, use importScripts. This is not a very
|
||||
//efficient use of importScripts, importScripts will block until
|
||||
//its script is downloaded and evaluated. However, if web workers
|
||||
//are in play, the expectation that a build has been done so that
|
||||
//only one script needs to be loaded anyway. This may need to be
|
||||
//reevaluated if other use cases become common.
|
||||
importScripts(url);
|
||||
|
||||
//Account for anonymous modules
|
||||
context.completeLoad(moduleName);
|
||||
//Account for anonymous modules
|
||||
context.completeLoad(moduleName);
|
||||
} catch (e) {
|
||||
context.onError(makeError('importscripts',
|
||||
'importScripts failed for ' +
|
||||
moduleName + ' at ' + url,
|
||||
e,
|
||||
[moduleName]));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1880,24 +1932,31 @@ var requirejs, require, define;
|
||||
//baseUrl, if it is not already set.
|
||||
dataMain = script.getAttribute('data-main');
|
||||
if (dataMain) {
|
||||
//Preserve dataMain in case it is a path (i.e. contains '?')
|
||||
mainScript = dataMain;
|
||||
|
||||
//Set final baseUrl if there is not already an explicit one.
|
||||
if (!cfg.baseUrl) {
|
||||
//Pull off the directory of data-main for use as the
|
||||
//baseUrl.
|
||||
src = dataMain.split('/');
|
||||
src = mainScript.split('/');
|
||||
mainScript = src.pop();
|
||||
subPath = src.length ? src.join('/') + '/' : './';
|
||||
|
||||
cfg.baseUrl = subPath;
|
||||
dataMain = mainScript;
|
||||
}
|
||||
|
||||
//Strip off any trailing .js since dataMain is now
|
||||
//Strip off any trailing .js since mainScript is now
|
||||
//like a module name.
|
||||
dataMain = dataMain.replace(jsSuffixRegExp, '');
|
||||
mainScript = mainScript.replace(jsSuffixRegExp, '');
|
||||
|
||||
//If mainScript is still a path, fall back to dataMain
|
||||
if (req.jsExtRegExp.test(mainScript)) {
|
||||
mainScript = dataMain;
|
||||
}
|
||||
|
||||
//Put the data-main script in the files to load.
|
||||
cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain];
|
||||
cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript];
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1925,12 +1984,13 @@ var requirejs, require, define;
|
||||
//This module may not have dependencies
|
||||
if (!isArray(deps)) {
|
||||
callback = deps;
|
||||
deps = [];
|
||||
deps = null;
|
||||
}
|
||||
|
||||
//If no name, and callback is a function, then figure out if it a
|
||||
//CommonJS thing with dependencies.
|
||||
if (!deps.length && isFunction(callback)) {
|
||||
if (!deps && isFunction(callback)) {
|
||||
deps = [];
|
||||
//Remove comments from the callback string,
|
||||
//look for require calls, and pull them into the dependencies,
|
||||
//but only if there are function args.
|
||||
@@ -1990,4 +2050,4 @@ var requirejs, require, define;
|
||||
|
||||
//Set up with config info.
|
||||
req(cfg);
|
||||
}(this));
|
||||
}(this));
|
||||
+259
-481
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="http://localhost/im_livechat/static/ext/static/js/require.js"></script>
|
||||
<script type="text/javascript" src='http://localhost/im_livechat/loader?p={"db":"test","channel":1}'></script>
|
||||
<script type="text/javascript" src="http://localhost/im_livechat/static/ext/static/lib/requirejs/require.js"></script>
|
||||
<script type="text/javascript" src='http://localhost/im_livechat/loader?p={"db":"testtrunk","channel":1}'></script>
|
||||
</head>
|
||||
<body style="height:100%; margin:0; padding:0;">
|
||||
<iframe src="http://openerp.com" height="100%" width=100%"></iframe>
|
||||
|
||||
Reference in New Issue
Block a user