odoo.define('web.framework', function (require) { "use strict"; var core = require('web.core'); var crash_manager = require('web.crash_manager'); var session = require('web.session'); var Widget = require('web.Widget'); var _t = core._t; var Spinner = window.Spinner; var messages_by_seconds = function() { return [ [0, _t("Loading...")], [20, _t("Still loading...")], [60, _t("Still loading...
Please be patient.")], [120, _t("Don't leave yet,
it's still loading...")], [300, _t("You may not believe it,
but the application is actually loading...")], [420, _t("Take a minute to get a coffee,
because it's loading...")], [3600, _t("Maybe you should consider reloading the application by pressing F5...")] ]; }; var Throbber = Widget.extend({ template: "Throbber", start: function() { var opts = { lines: 13, // The number of lines to draw length: 7, // The length of each line width: 4, // The line thickness radius: 10, // The radius of the inner circle rotate: 0, // The rotation offset color: '#FFF', // #rgb or #rrggbb speed: 1, // Rounds per second trail: 60, // Afterglow percentage shadow: false, // Whether to render a shadow hwaccel: false, // Whether to use hardware acceleration className: 'spinner', // The CSS class to assign to the spinner zIndex: 2e9, // The z-index (defaults to 2000000000) top: 'auto', // Top position relative to parent in px left: 'auto' // Left position relative to parent in px }; this.spin = new Spinner(opts).spin(this.$el[0]); this.start_time = new Date().getTime(); this.act_message(); }, act_message: function() { var self = this; setTimeout(function() { if (self.isDestroyed()) return; var seconds = (new Date().getTime() - self.start_time) / 1000; var mes; _.each(messages_by_seconds(), function(el) { if (seconds >= el[0]) mes = el[1]; }); self.$(".oe_throbber_message").html(mes); self.act_message(); }, 1000); }, destroy: function() { if (this.spin) this.spin.stop(); this._super(); }, }); // special tweak for the web client var old_async_when = $.async_when; $.async_when = function() { if (session.synch) return $.when.apply(this, arguments); else return old_async_when.apply(this, arguments); }; /** Setup blockui */ if ($.blockUI) { $.blockUI.defaults.baseZ = 1100; $.blockUI.defaults.message = '
'; $.blockUI.defaults.css.border = '0'; $.blockUI.defaults.css["background-color"] = ''; } var throbbers = []; function blockUI () { var tmp = $.blockUI.apply($, arguments); var throbber = new Throbber(); throbbers.push(throbber); throbber.appendTo($(".oe_blockui_spin_container")); return tmp; } function unblockUI () { _.invoke(throbbers, 'destroy'); throbbers = []; return $.unblockUI.apply($, arguments); } /** * Redirect to url by replacing window.location * If wait is true, sleep 1s and wait for the server i.e. after a restart. */ function redirect (url, wait) { // Dont display a dialog if some xmlhttprequest are in progress crash_manager.disable(); var load = function() { var old = "" + window.location; var old_no_hash = old.split("#")[0]; var url_no_hash = url.split("#")[0]; location.assign(url); if (old_no_hash === url_no_hash) { location.reload(true); } }; var wait_server = function() { session.rpc("/web/webclient/version_info", {}).done(load).fail(function() { setTimeout(wait_server, 250); }); }; if (wait) { setTimeout(wait_server, 1000); } else { load(); } } // * Client action to reload the whole interface. // * If params.menu_id, it opens the given menu entry. // * If params.wait, reload will wait the openerp server to be reachable before reloading function Reload(parent, action) { var params = action.params || {}; var menu_id = params.menu_id || false; var l = window.location; var sobj = $.deparam(l.search.substr(1)); if (params.url_search) { sobj = _.extend(sobj, params.url_search); } var search = '?' + $.param(sobj); var hash = l.hash; if (menu_id) { hash = "#menu_id=" + menu_id; } var url = l.protocol + "//" + l.host + l.pathname + search + hash; redirect(url, params.wait); } core.action_registry.add("reload", Reload); /** * Client action to go back home. */ function Home (parent, action) { var url = '/' + (window.location.search || ''); redirect(url, action && action.params && action.params.wait); } core.action_registry.add("home", Home); /** * Client action to go back in breadcrumb history. * If can't go back in history stack, will go back to home. */ function HistoryBack (parent) { parent.history_back().fail(function () { Home(parent); }); } core.action_registry.add("history_back", HistoryBack); function login() { redirect('/web/login'); } core.action_registry.add("login", login); function logout() { redirect('/web/session/logout'); } core.action_registry.add("logout", logout); /** * Client action to refresh the session context (making sure * HTTP requests will have the right one) then reload the * whole interface. */ function ReloadContext (parent, action) { // side-effect of get_session_info is to refresh the session context session.rpc("/web/session/get_session_info", {}).then(function() { Reload(parent, action); }); } core.action_registry.add("reload_context", ReloadContext); // nvd3 customization //------------------------------------------------------------------------- if ('nv' in window) { nv.dev = false; // sets nvd3 library in production mode // monkey patch nvd3 to allow removing eventhandler on windowresize events // see https://github.com/novus/nvd3/pull/396 for more details // Adds a resize listener to the window. nv.utils.onWindowResize = function(fun) { if (fun === null) return; window.addEventListener('resize', fun); }; // Backwards compatibility with current API. nv.utils.windowResize = nv.utils.onWindowResize; // Removes a resize listener from the window. nv.utils.offWindowResize = function(fun) { if (fun === null) return; window.removeEventListener('resize', fun); }; // monkey patch nvd3 to prevent crashes when user changes view and nvd3 tries // to remove tooltips after 500 ms... seriously nvd3, what were you thinking? nv.tooltip.cleanup = function () { $('.nvtooltip').remove(); }; } // Bootstrap customization //------------------------------------------------------------------------- /* Bootstrap defaults overwrite */ $.fn.tooltip.Constructor.DEFAULTS.placement = 'auto top'; $.fn.tooltip.Constructor.DEFAULTS.html = true; $.fn.tooltip.Constructor.DEFAULTS.trigger = 'hover focus click'; $.fn.tooltip.Constructor.DEFAULTS.container = 'body'; $.fn.tooltip.Constructor.DEFAULTS.delay = { show: 1000, hide: 0 }; //overwrite bootstrap tooltip method to prevent showing 2 tooltip at the same time var bootstrap_show_function = $.fn.tooltip.Constructor.prototype.show; $.fn.modal.Constructor.prototype.enforceFocus = function () { }; $.fn.tooltip.Constructor.prototype.show = function () { $('.tooltip').remove(); //the following fix the bug when using placement //auto and the parent element does not exist anymore resulting in //an error. This should be remove once we updade bootstrap to a version that fix the bug //edit: bug has been fixed here : https://github.com/twbs/bootstrap/pull/13752 var e = $.Event('show.bs.' + this.type); var inDom = $.contains(document.documentElement, this.$element[0]); if (e.isDefaultPrevented() || !inDom) return; return bootstrap_show_function.call(this); }; // jquery customization //------------------------------------------------------------------------- jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) { return function( elem ) { return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0; }; }); /** Custom jQuery plugins */ $.fn.getAttributes = function() { var o = {}; if (this.length) { for (var attr, i = 0, attrs = this[0].attributes, l = attrs.length; i < l; i++) { attr = attrs.item(i); o[attr.nodeName] = attr.value; } } return o; }; $.fn.openerpClass = function(additionalClass) { // This plugin should be applied on top level elements additionalClass = additionalClass || ''; if (!!$.browser.msie) { additionalClass += ' openerp_ie'; } return this.each(function() { $(this).addClass('openerp ' + additionalClass); }); }; $.fn.openerpBounce = function() { return this.each(function() { $(this).css('box-sizing', 'content-box').effect('bounce', {distance: 18, times: 5}, 250); }); }; // jquery autocomplete tweak to allow html and classnames var proto = $.ui.autocomplete.prototype, initSource = proto._initSource; function filter( array, term ) { var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" ); return $.grep( array, function(value_) { return matcher.test( $( "
" ).html( value_.label || value_.value || value_ ).text() ); }); } $.extend( proto, { _initSource: function() { if ( this.options.html && $.isArray(this.options.source) ) { this.source = function( request, response ) { response( filter( this.options.source, request.term ) ); }; } else { initSource.call( this ); } }, _renderItem: function( ul, item) { return $( "
  • " ) .data( "item.autocomplete", item ) .append( $( "" )[ this.options.html ? "html" : "text" ]( item.label ) ) .appendTo( ul ) .addClass(item.classname); } }); /** * Private function that triggers an event on core.bus * @param {htmlString or Element or Array or jQuery} [content] the content that * has been attached in the DOM */ function _notify (content) { core.bus.trigger('DOM_updated', content); } /** * Appends content in a jQuery object and optionnally triggers an event * @param {jQuery} [$target] the node where content will be appended * @param {htmlString or Element or Array or jQuery} [content] DOM element, * array of elements, HTML string or jQuery object to append to $target * @param {jQuery} [trigger] true to trigger an event, false otherwise */ function append ($target, content, trigger) { $target.append(content); if (trigger) { _notify(content); } } /** * Prepends content in a jQuery object and optionnally triggers an event * @param {jQuery} [$target] the node where content will be prepended * @param {htmlString or Element or Array or jQuery} [content] DOM element, * array of elements, HTML string or jQuery object to prepend to $target * @param {jQuery} [trigger] true to trigger an event, false otherwise */ function prepend ($target, content, trigger) { $target.prepend(content); if (trigger) { _notify(content); } } /** * Returns the distance between a DOM element and the top-left corner of the window * @param {element} [e] the DOM element * @return {Object} the left and top distances in pixels */ function getPosition(e) { var position = {left: 0, top: 0}; while (e) { position.left += e.offsetLeft; position.top += e.offsetTop; e = e.offsetParent; } return position; } return { blockUI: blockUI, unblockUI: unblockUI, redirect: redirect, append: append, prepend: prepend, getPosition: getPosition, }; }); odoo.define('web.IFrameWidget', function (require) { "use strict"; var Widget = require('web.Widget'); /** * Generic widget to create an iframe that listens for clicks * * It should be extended by overwritting the methods: * init: function(parent) { * this._super(parent, * }, * iframe_clicked: function(e){ * filter the clicks you want to use and apply * an action on it * } */ var IFrameWidget = Widget.extend({ tagName: 'iframe', init: function(parent, url) { this._super(parent); this.url = url; }, start: function() { this.$el.css({height: '100%', width: '100%', border: 0}); this.$el.attr({src: this.url}); this.$el.on("load", this.bind_events.bind(this)); return this._super(); }, bind_events: function(){ this.$el.contents().click(this.iframe_clicked.bind(this)); }, iframe_clicked: function(e){ } }); return IFrameWidget; });