diff --git a/addons/website/static/lib/bootstrap-tour/bootstrap-tour.css b/addons/website/static/lib/bootstrap-tour/bootstrap-tour.css
deleted file mode 100755
index 72c6e8cbab7..00000000000
--- a/addons/website/static/lib/bootstrap-tour/bootstrap-tour.css
+++ /dev/null
@@ -1,43 +0,0 @@
-.tour-backdrop {
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 1009;
- background-color: #000;
- opacity: 0.8;
-}
-.tour-step-backdrop {
- position: relative;
- z-index: 1011;
-}
-.tour-step-background {
- position: absolute;
- z-index: 1010;
- background: #fff;
- border-radius: 6px;
-}
-.popover[class*="tour-"] .popover-navigation {
- padding: 9px 14px;
-}
-.popover[class*="tour-"] .popover-navigation *[data-role=end] {
- float: right;
-}
-.popover[class*="tour-"] .popover-navigation *[data-role=prev],
-.popover[class*="tour-"] .popover-navigation *[data-role=next],
-.popover[class*="tour-"] .popover-navigation *[data-role=end] {
- cursor: pointer;
-}
-.popover[class*="tour-"] .popover-navigation *[data-role=prev].disabled,
-.popover[class*="tour-"] .popover-navigation *[data-role=next].disabled,
-.popover[class*="tour-"] .popover-navigation *[data-role=end].disabled {
- cursor: default;
-}
-.popover[class*="tour-"].orphan {
- position: fixed;
- margin-top: 0;
-}
-.popover[class*="tour-"].orphan .arrow {
- display: none;
-}
diff --git a/addons/website/static/lib/bootstrap-tour/bootstrap-tour.js b/addons/website/static/lib/bootstrap-tour/bootstrap-tour.js
deleted file mode 100755
index 8b45d6da9ee..00000000000
--- a/addons/website/static/lib/bootstrap-tour/bootstrap-tour.js
+++ /dev/null
@@ -1,559 +0,0 @@
-/* ===========================================================
-# bootstrap-tour - v0.6.1
-# http://bootstraptour.com
-# ==============================================================
-# Copyright 2012-2013 Ulrich Sossou
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-*/
-(function() {
- (function($, window) {
- var Tour, document;
- document = window.document;
- Tour = (function() {
- function Tour(options) {
- this._options = $.extend({
- name: "tour",
- container: "body",
- keyboard: true,
- storage: window.localStorage,
- debug: false,
- backdrop: false,
- redirect: true,
- orphan: false,
- basePath: "",
- template: "
").parent().html();
- $element.popover({
- placement: step.placement,
- trigger: "manual",
- title: step.title,
- content: step.content,
- html: true,
- animation: step.animation,
- container: step.container,
- template: step.template,
- selector: step.element
- }).popover("show");
- $tip = $element.data("bs.popover") ? $element.data("bs.popover").tip() : $element.data("popover").tip();
- $tip.attr("id", step.id);
- this._scrollIntoView($tip);
- this._reposition($tip, step);
- if (isOrphan) {
- return this._center($tip);
- }
- };
-
- Tour.prototype._reposition = function($tip, step) {
- var offsetBottom, offsetHeight, offsetRight, offsetWidth, originalLeft, originalTop, tipOffset;
- offsetWidth = $tip[0].offsetWidth;
- offsetHeight = $tip[0].offsetHeight;
- tipOffset = $tip.offset();
- originalLeft = tipOffset.left;
- originalTop = tipOffset.top;
- offsetBottom = $(document).outerHeight() - tipOffset.top - $tip.outerHeight();
- if (offsetBottom < 0) {
- tipOffset.top = tipOffset.top + offsetBottom;
- }
- offsetRight = $("html").outerWidth() - tipOffset.left - $tip.outerWidth();
- if (offsetRight < 0) {
- tipOffset.left = tipOffset.left + offsetRight;
- }
- if (tipOffset.top < 0) {
- tipOffset.top = 0;
- }
- if (tipOffset.left < 0) {
- tipOffset.left = 0;
- }
- $tip.offset(tipOffset);
- if (step.placement === "bottom" || step.placement === "top") {
- if (originalLeft !== tipOffset.left) {
- return this._replaceArrow($tip, (tipOffset.left - originalLeft) * 2, offsetWidth, "left");
- }
- } else {
- if (originalTop !== tipOffset.top) {
- return this._replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, "top");
- }
- }
- };
-
- Tour.prototype._center = function($tip) {
- return $tip.css("top", $(window).outerHeight() / 2 - $tip.outerHeight() / 2);
- };
-
- Tour.prototype._replaceArrow = function($tip, delta, dimension, position) {
- return $tip.find(".arrow").css(position, delta ? 50 * (1 - delta / dimension) + "%" : "");
- };
-
- Tour.prototype._scrollIntoView = function(tip) {
- return $("html, body").stop().animate({
- scrollTop: Math.ceil(tip.offset().top - ($(window).height() / 2))
- });
- };
-
- Tour.prototype._onResize = function(callback, timeout) {
- return $(window).on("resize.tour-" + this._options.name, function() {
- clearTimeout(timeout);
- return timeout = setTimeout(callback, 100);
- });
- };
-
- Tour.prototype._setupKeyboardNavigation = function() {
- var _this = this;
- if (this._options.keyboard) {
- return $(document).on("keyup.tour-" + this._options.name, function(e) {
- if (!e.which) {
- return;
- }
- switch (e.which) {
- case 39:
- e.preventDefault();
- if (_this._current < _this._steps.length - 1) {
- return _this.next();
- } else {
- return _this.end();
- }
- break;
- case 37:
- e.preventDefault();
- if (_this._current > 0) {
- return _this.prev();
- }
- break;
- case 27:
- e.preventDefault();
- return _this.end();
- }
- });
- }
- };
-
- Tour.prototype._makePromise = function(result) {
- if (result && $.isFunction(result.then)) {
- return result;
- } else {
- return null;
- }
- };
-
- Tour.prototype._callOnPromiseDone = function(promise, cb, arg) {
- var _this = this;
- if (promise) {
- return promise.then(function(e) {
- return cb.call(_this, arg);
- });
- } else {
- return cb.call(this, arg);
- }
- };
-
- Tour.prototype._showBackdrop = function(element) {
- if (this.backdrop.overlay !== null) {
- return;
- }
- this._showOverlay();
- if (element != null) {
- return this._showOverlayElement(element);
- }
- };
-
- Tour.prototype._hideBackdrop = function() {
- if (this.backdrop.overlay === null) {
- return;
- }
- if (this.backdrop.$element) {
- this._hideOverlayElement();
- }
- return this._hideOverlay();
- };
-
- Tour.prototype._showOverlay = function() {
- this.backdrop = $("
", {
- "class": "tour-backdrop"
- });
- return $("body").append(this.backdrop);
- };
-
- Tour.prototype._hideOverlay = function() {
- this.backdrop.remove();
- return this.backdrop.overlay = null;
- };
-
- Tour.prototype._showOverlayElement = function(element) {
- var $background, $element, offset;
- $element = $(element);
- $background = $("
");
- offset = $element.offset();
- offset.top = offset.top;
- offset.left = offset.left;
- $background.width($element.innerWidth()).height($element.innerHeight()).addClass("tour-step-background").offset(offset);
- $element.addClass("tour-step-backdrop");
- $("body").append($background);
- this.backdrop.$element = $element;
- return this.backdrop.$background = $background;
- };
-
- Tour.prototype._hideOverlayElement = function() {
- this.backdrop.$element.removeClass("tour-step-backdrop");
- this.backdrop.$background.remove();
- this.backdrop.$element = null;
- return this.backdrop.$background = null;
- };
-
- return Tour;
-
- })();
- return window.Tour = Tour;
- })(jQuery, window);
-
-}).call(this);
diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css
index 4e9038837f3..7d8b1e609e8 100644
--- a/addons/website/static/src/css/editor.css
+++ b/addons/website/static/src/css/editor.css
@@ -518,10 +518,30 @@ div.tour-backdrop {
z-index: 2009;
}
-.popover.tour {
- z-index: 2010;
+.popover.tour.orphan .arrow {
+ display: none;
+}
+.popover.tour .popover-navigation {
+ padding: 9px 14px;
+}
+.popover.tour .popover-navigation *[data-role="end"] {
+ float: right;
+}
+.popover.tour .popover-navigation *[data-role="next"], .popover.tour .popover-navigation *[data-role="end"] {
+ cursor: pointer;
}
.popover.fixed {
position: fixed;
}
+
+.tour-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1100;
+ background-color: black;
+ opacity: 0.8;
+}
diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass
index 53eb89a4341..d39b6abd453 100644
--- a/addons/website/static/src/css/editor.sass
+++ b/addons/website/static/src/css/editor.sass
@@ -451,9 +451,26 @@ $editorbar_height: 30px
div.tour-backdrop
z-index: 2009
.popover.tour
- z-index: 2010
+ &.orphan .arrow
+ display: none
+ .popover-navigation
+ padding: 9px 14px
+ *[data-role="end"]
+ float: right
+ *[data-role="next"],*[data-role="end"]
+ cursor: pointer
.popover.fixed
position: fixed
+.tour-backdrop
+ position: fixed
+ top: 0
+ right: 0
+ bottom: 0
+ left: 0
+ z-index: 1100
+ background-color: #000
+ opacity: 0.8
+
// }}}
diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js
index 2119d05f650..be69c66b615 100644
--- a/addons/website/static/src/js/website.editor.js
+++ b/addons/website/static/src/js/website.editor.js
@@ -545,7 +545,7 @@
observer.disconnect();
var editor = this.rte.editor;
- var root = editor.element.$;
+ var root = editor.element && editor.element.$;
editor.destroy();
// FIXME: select editables then filter by dirty?
var defs = this.rte.fetch_editables(root)
diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js
index 0177e8b8b64..8f49409924d 100644
--- a/addons/website/static/src/js/website.snippets.editor.js
+++ b/addons/website/static/src/js/website.snippets.editor.js
@@ -362,7 +362,6 @@
},
clean_for_save: function () {
var self = this;
-
$("*[contentEditable], *[attributeEditable]")
.removeAttr('contentEditable')
.removeAttr('attributeEditable');
diff --git a/addons/website/static/src/js/website.tour.banner.js b/addons/website/static/src/js/website.tour.banner.js
index d401cc40128..3d55220084e 100644
--- a/addons/website/static/src/js/website.tour.banner.js
+++ b/addons/website/static/src/js/website.tour.banner.js
@@ -4,127 +4,116 @@
var website = openerp.website;
var _t = openerp._t;
- website.EditorBar.include({
- start: function () {
- this.registerTour(new website.Tour.Banner(this));
- return this._super();
- },
- });
-
- website.Tour.Banner = website.Tour.extend({
+ website.Tour.register({
id: 'banner',
- name: "Build a page",
+ name: _t("Build a page"),
path: '/page/website.homepage',
- init: function () {
- var self = this;
- self.steps = [
- {
- title: _t("Welcome to your website!"),
- content: _t("This tutorial will guide you to build your home page. We will start by adding a banner."),
- popover: { next: _t("Start Tutorial"), end: _t("Skip It") },
- },
- {
- waitNot: '.popover.tour',
- element: 'button[data-action=edit]',
- placement: 'bottom',
- title: _t("Edit this page"),
- content: _t("Every page of your website can be modified through the
Edit button."),
- popover: { fixed: true },
- },
- {
- element: 'button[data-action=snippet]',
- placement: 'bottom',
- title: _t("Insert building blocks"),
- content: _t("Click here to insert blocks of content in the page."),
- popover: { fixed: true },
- },
- {
- snippet: '#snippet_structure .oe_snippet:first',
- placement: 'bottom',
- title: _t("Drag & Drop a Banner"),
- content: _t("Drag the Banner block and drop it in your page."),
- popover: { fixed: true },
- },
- {
- waitFor: '.oe_overlay_options .oe_options:visible',
- element: '#wrap .carousel:first div.carousel-content',
- placement: 'top',
- title: _t("Customize banner's text"),
- content: _t("Click in the text and start editing it."),
- sampleText: 'Here, a customized text',
- },
- {
- waitNot: '#wrap .carousel:first div.carousel-content:has(h2:'+
- 'containsExact('+_t('Your Banner Title')+')):has(h3:'+
- 'containsExact('+_t('Click to customize this text')+'))',
- element: '.oe_snippet_parent:visible',
- placement: 'bottom',
- title: _t("Get banner properties"),
- content: _t("Select the parent container to get the global options of the banner."),
- popover: { fixed: true },
- },
- {
- element: '.oe_overlay_options .oe_options:visible',
- placement: 'left',
- title: _t("Customize the banner"),
- content: _t("Customize any block through this menu. Try to change the background of the banner."),
- popover: { next: _t("Continue") },
- },
- {
- waitNot: '.popover.tour',
- element: 'button[data-action=snippet]',
- placement: 'bottom',
- title: _t("Add Another Block"),
- content: _t("Let's add another building block to your page."),
- popover: { fixed: true },
- },
- {
- snippet: '#snippet_structure .oe_snippet:eq(6)',
- placement: 'bottom',
- title: _t("Drag & Drop This Block"),
- content: _t("Drag the
'Features' block and drop it below the banner."),
- popover: { fixed: true },
- },
- {
- waitFor: '.oe_overlay_options .oe_options:visible',
- element: 'button[data-action=save]',
- placement: 'right',
- title: _t("Save your modifications"),
- content: _t("Publish your page by clicking on the
'Save' button."),
- popover: { fixed: true },
- },
- {
- waitFor: 'button[data-action=edit]:visible',
- title: _t("Good Job!"),
- content: _t("Well done, you created your homepage."),
- popover: { next: _t("Continue") },
- },
- {
- waitNot: '.popover.tour',
- element: 'a[data-action=show-mobile-preview]',
- placement: 'bottom',
- title: _t("Test Your Mobile Version"),
- content: _t("Let's check how your homepage looks like on mobile devices."),
- popover: { fixed: true },
- },
- {
- element: '.modal:has(#mobile-viewport) button[data-dismiss=modal]',
- placement: 'right',
- title: _t("Check Mobile Preview"),
- content: _t("Scroll to check rendering and then close the mobile preview."),
- popover: { next: _t("Continue") },
- },
- {
- waitNot: '.modal',
- element: '#content-menu-button',
- placement: 'left',
- title: _t("Add new pages and menus"),
- content: _t("The 'Content' menu allows you to add pages or add the top menu."),
- popover: { next: _t("Close Tutorial") },
- },
- ];
- return this._super();
- },
+ steps: [
+ {
+ title: _t("Welcome to your website!"),
+ content: _t("This tutorial will guide you to build your home page. We will start by adding a banner."),
+ popover: { next: _t("Start Tutorial"), end: _t("Skip It") },
+ },
+ {
+ waitNot: '.popover.tour',
+ element: 'button[data-action=edit]',
+ placement: 'bottom',
+ title: _t("Edit this page"),
+ content: _t("Every page of your website can be modified through the
Edit button."),
+ popover: { fixed: true },
+ },
+ {
+ element: 'button[data-action=snippet]',
+ placement: 'bottom',
+ title: _t("Insert building blocks"),
+ content: _t("Click here to insert blocks of content in the page."),
+ popover: { fixed: true },
+ },
+ {
+ snippet: '#snippet_structure .oe_snippet:first',
+ placement: 'bottom',
+ title: _t("Drag & Drop a Banner"),
+ content: _t("Drag the Banner block and drop it in your page."),
+ popover: { fixed: true },
+ },
+ {
+ waitFor: '.oe_overlay_options .oe_options:visible',
+ element: '#wrap .carousel:first div.carousel-content',
+ placement: 'top',
+ title: _t("Customize banner's text"),
+ content: _t("Click in the text and start editing it."),
+ sampleText: 'Here, a customized text',
+ },
+ {
+ waitNot: '#wrap .carousel:first div.carousel-content:has(h2:'+
+ 'containsExact('+_t('Your Banner Title')+')):has(h3:'+
+ 'containsExact('+_t('Click to customize this text')+'))',
+ element: '.oe_snippet_parent:visible',
+ placement: 'bottom',
+ title: _t("Get banner properties"),
+ content: _t("Select the parent container to get the global options of the banner."),
+ popover: { fixed: true },
+ },
+ {
+ element: '.oe_overlay_options .oe_options:visible',
+ placement: 'left',
+ title: _t("Customize the banner"),
+ content: _t("Customize any block through this menu. Try to change the background of the banner."),
+ popover: { next: _t("Continue") },
+ },
+ {
+ waitNot: '.popover.tour',
+ element: 'button[data-action=snippet]',
+ placement: 'bottom',
+ title: _t("Add Another Block"),
+ content: _t("Let's add another building block to your page."),
+ popover: { fixed: true },
+ },
+ {
+ snippet: '#snippet_structure .oe_snippet:eq(6)',
+ placement: 'bottom',
+ title: _t("Drag & Drop This Block"),
+ content: _t("Drag the
'Features' block and drop it below the banner."),
+ popover: { fixed: true },
+ },
+ {
+ waitFor: '.oe_overlay_options .oe_options:visible',
+ element: 'button[data-action=save]',
+ placement: 'right',
+ title: _t("Save your modifications"),
+ content: _t("Publish your page by clicking on the
'Save' button."),
+ popover: { fixed: true },
+ },
+ {
+ waitFor: 'button[data-action=edit]:visible',
+ title: _t("Good Job!"),
+ content: _t("Well done, you created your homepage."),
+ popover: { next: _t("Continue") },
+ },
+ {
+ waitNot: '.popover.tour',
+ element: 'a[data-action=show-mobile-preview]',
+ placement: 'bottom',
+ title: _t("Test Your Mobile Version"),
+ content: _t("Let's check how your homepage looks like on mobile devices."),
+ popover: { fixed: true },
+ },
+ {
+ element: '.modal:has(#mobile-viewport) button[data-dismiss=modal]',
+ placement: 'right',
+ title: _t("Check Mobile Preview"),
+ content: _t("Scroll to check rendering and then close the mobile preview."),
+ popover: { next: _t("Continue") },
+ },
+ {
+ waitNot: '.modal',
+ element: '#content-menu-button',
+ placement: 'left',
+ title: _t("Add new pages and menus"),
+ content: _t("The 'Content' menu allows you to add pages or add the top menu."),
+ popover: { next: _t("Close Tutorial") },
+ },
+ ]
});
}());
diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js
index 911b6c7431a..2a661b4cc9b 100644
--- a/addons/website/static/src/js/website.tour.js
+++ b/addons/website/static/src/js/website.tour.js
@@ -6,7 +6,7 @@ if (typeof openerp === "undefined") {
var error = "openerp is undefined"
+ "\nhref: " + window.location.href
+ "\nreferrer: " + document.referrer
- + "\nlocalStorage: " + JSON.stringify(window.localStorage);
+ + "\nlocalStorage: " + window.localStorage.getItem("tour");
if (typeof $ !== "undefined") {
error += '\n\n' + $("body").html();
}
@@ -15,7 +15,7 @@ if (typeof openerp === "undefined") {
var website = window.openerp.website;
-// don't rewrite website.Tour in test mode
+// don't rewrite T in test mode
if (typeof website.Tour !== "undefined") {
return;
}
@@ -23,40 +23,26 @@ if (typeof website.Tour !== "undefined") {
// don't need template to use bootstrap Tour in automatic mode
if (typeof QWeb2 !== "undefined") {
website.add_template_file('/website/static/src/xml/website.tour.xml');
-
}
-// don't need to use bootstrap Tour to launch an automatic tour
-function bootstrap_tour_stub () {
- if (typeof Tour === "undefined") {
- window.Tour = function Tour() {};
- Tour.prototype.addSteps = function () {};
- Tour.prototype.end = function () {};
- Tour.prototype.goto = function () {};
- }
-}
-
-
-
if (website.EditorBar) {
website.EditorBar.include({
tours: [],
start: function () {
var self = this;
var menu = $('#help-menu');
- _.each(this.tours, function (tour) {
+ _.each(T.tours, function (tour) {
+ if (tour.mode === "test") {
+ return;
+ }
var $menuItem = $($.parseHTML('
'+tour.name+''));
$menuItem.click(function () {
- tour.reset();
- tour.run();
+ T.reset();
+ T.run(tour.id);
});
menu.append($menuItem);
});
return this._super();
- },
- registerTour: function (tour) {
- website.Tour.add(tour);
- this.tours.push(tour);
}
});
}
@@ -94,351 +80,401 @@ $.ajaxSetup({
}
});
-website.Tour = openerp.Class.extend({
- steps: [],
- defaultDelay: 50, //ms
- defaultOverLaps: 5000, //ms
- localStorage: window.localStorage,
- init: function () {},
+/////////////////////////////////////////////////
- run: function (automatic) {
- this.reset();
+var localStorage = window.localStorage;
- for (var k in this.localStorage) {
- if (!k.indexOf("tour-") && k.indexOf("-test") > -1) return;
- }
-
- website.Tour.busy = true;
-
- if (automatic) {
- this.localStorage.setItem("tour-"+this.id+"-test-automatic", true);
+var T = website.Tour = {
+ tours: {},
+ defaultDelay: 50,
+ retryRunningDelay: 1000,
+ errorDelay: 5000,
+ state: null,
+ $element: null,
+ timer: null,
+ testtimer: null,
+ currentTimer: null,
+ register: function (tour) {
+ if (tour.mode !== "test") tour.mode = "tutorial";
+ T.tours[tour.id] = tour;
+ },
+ run: function (tour_id, mode) {
+ var tour = T.tours[tour_id];
+ this.time = new Date().getTime();
+ if (tour.path && !window.location.href.match(new RegExp("("+T.getLang()+")?"+tour.path+"#?$", "i"))) {
+ var href = "/"+T.getLang()+tour.path;
+ console.log("Tour Begin from run method (redirection to "+href+")");
+ T.saveState(tour.id, mode || tour.mode, -1);
+ window.location.href = href;
} else {
- this.localStorage.removeItem("tour-"+this.id+"-test-automatic");
+ console.log("Tour Begin from run method");
+ T.saveState(tour.id, mode || tour.mode, 0);
+ T.running();
}
- this.automatic = automatic;
-
- if (this.path) {
- // redirect to begin of the tour in function of the language
- if (!this.testUrl(this.path+"(#.*)?$")) {
- var path = this.path.split('#');
- window.location.href = "/"+this.getLang()+path[0] + "#tutorial."+this.id+"=true&" + path.slice(1, path.length).join("#");
- return;
- }
- }
-
- var self = this;
- this.localStorage.setItem("tour-"+this.id+"-test", 0);
- website.Tour.waitReady.call(this, function () {self._running();});
},
- running: function () {
- var self = this;
- if (+this.localStorage.getItem("tour-"+this.id+"-test") >= this.steps.length-1) {
- this.endTour();
+ registerSteps: function (tour) {
+ if (tour.register) {
return;
}
+ tour.register = true;
- if (website.Tour.is_busy()) return;
+ for (var index=0, len=tour.steps.length; index
0 && tour.steps[index-1] &&
+ tour.steps[index-1].popover && tour.steps[index-1].popover.next) {
+ step.waitNot = '.popover.tour.fade.in:visible';
}
- return;
- }
-
- var self = this;
- website.Tour.waitReady.call(this, function () {self._running();});
- },
- _running: function () {
- var stepId = this.localStorage.getItem("tour-"+this.id+"-test");
-
- if (stepId != null) {
- this.registerTour();
- this.nextStep(stepId, this.automatic ? this.autoNextStep : null, this.automatic ? this.defaultOverLaps : null);
- }
- },
-
- reset: function () {
- website.Tour.busy = false;
- for (var k in this.steps) {
- this.steps[k].busy = false;
- }
- clearTimeout(self.timer);
- clearTimeout(self.testtimer);
-
- for (var k in this.localStorage) {
- if (!k.indexOf("tour-") || !k.indexOf(this.id)) {
- this.localStorage.removeItem(k);
- }
- }
-
- $('.popover.tour').remove();
- },
-
- getLang: function () {
- return $("html").attr("lang").replace(/-/, '_');
- },
- testUrl: function (url) {
- return new RegExp("(/"+this.getLang()+")?"+url, "i").test(window.location.href);
- },
- testPathUrl: function () {
- if (!this.testPath || this.testUrl(this.testPath)) return true;
- },
- checkRunningUrl: function () {
- if (window.location.hash.indexOf("tutorial."+this.id+"=true") > -1) {
- this.localStorage.setItem("tour-"+this.id+"-test", 0);
- window.location.hash = window.location.hash.replace(/tutorial.+=true&?/, '');
- }
- },
-
- registerTour: function () {
- if (this.automatic) {
- bootstrap_tour_stub();
- }
- this.tour = new Tour({
- name: this.id,
- storage: this.tourStorage,
- keyboard: false,
- template: this.popover(),
- onHide: function () {
- window.scrollTo(0, 0);
- }
- });
- this.registerSteps();
- },
- registerSteps: function () {
- for (var index=0, len=this.steps.length; index 0 && this.steps[index-1] &&
- this.steps[index-1].popover && this.steps[index-1].popover.next) {
- step.waitNot = '.popover.tour:visible';
- }
- if (!step.waitFor && index > 0 && this.steps[index-1].snippet) {
+ if (!step.waitFor && index > 0 && tour.steps[index-1].snippet) {
step.waitFor = '.oe_overlay_options .oe_options:visible';
}
- step._title = step._title || step.title;
- step.title = this.popoverTitle({ title: step._title });
- step.template = step.template || this.popover( step.popover );
- if (!step.element) step.orphan = true;
- if (step.snippet) {
+ var snippet = step.element && step.element.match(/#oe_snippets (.*) \.oe_snippet_thumbnail/);
+ if (snippet) {
+ step.snippet = snippet[1];
+ } else if (step.snippet) {
step.element = '#oe_snippets '+step.snippet+' .oe_snippet_thumbnail';
}
+ if (!step.element) {
+ step.element = "body";
+ step.orphan = true;
+ step.backdrop = true;
+ }
}
-
- if (this.steps[index-1] &&
- this.steps[index-1].popover && this.steps[index-1].popover.next) {
+ if (tour.steps[index-1] &&
+ tour.steps[index-1].popover && tour.steps[index-1].popover.next) {
var step = {
- stepId: ""+index,
- waitNot: '.popover.tour:visible'
+ _title: "",
+ id: index,
+ waitNot: '.popover.tour.fade.in:visible'
};
- this.steps.push(step);
+ tour.steps.push(step);
}
- this.tour.addSteps(this.steps);
+ // rendering bootstrap tour and popover
+ if (tour.mode !== "test") {
+ for (var index=0, len=tour.steps.length; index ');
+ }
+
+ if (step.backdrop || $element.parents("#website-top-navbar, .modal").size()) {
+ $tip.css("z-index", 2010);
+ }
+
+ // button click event
+ $tip.find("button")
+ .one("click", function () {
+ step.busy = true;
+ if (!$(this).is("[data-role='next']")) {
+ clearTimeout(T.timer);
+ T.endTour();
+ }
+ T.closePopover();
+ });
+
+ T.repositionPopover();
+ },
+ repositionPopover: function() {
+ var popover = T.$element.data("bs.popover");
+ var $tip = T.$element.data("bs.popover").tip();
+
+ if (popover.options.orphan) {
+ return $tip.css("top", $(window).outerHeight() / 2 - $tip.outerHeight() / 2);
+ }
+
+ var offsetBottom, offsetHeight, offsetRight, offsetWidth, originalLeft, originalTop, tipOffset;
+ offsetWidth = $tip[0].offsetWidth;
+ offsetHeight = $tip[0].offsetHeight;
+ tipOffset = $tip.offset();
+ originalLeft = tipOffset.left;
+ originalTop = tipOffset.top;
+ offsetBottom = $(document).outerHeight() - tipOffset.top - $tip.outerHeight();
+ if (offsetBottom < 0) {
+ tipOffset.top = tipOffset.top + offsetBottom;
+ }
+ offsetRight = $("html").outerWidth() - tipOffset.left - $tip.outerWidth();
+ if (offsetRight < 0) {
+ tipOffset.left = tipOffset.left + offsetRight;
+ }
+ if (tipOffset.top < 0) {
+ tipOffset.top = 0;
+ }
+ if (tipOffset.left < 0) {
+ tipOffset.left = 0;
+ }
+ $tip.offset(tipOffset);
+ if (popover.options.placement === "bottom" || popover.options.placement === "top") {
+ var left = T.$element.offset().left + T.$element.outerWidth()/2 - tipOffset.left;
+ $tip.find(".arrow").css("left", left ? left + "px" : "");
+ } else if (popover.options.placement !== "auto") {
+ var top = T.$element.offset().top + T.$element.outerHeight()/2 - tipOffset.top;
+ $tip.find(".arrow").css("top", top ? top + "px" : "");
+ }
+ },
+ popoverTitle: function (tour, options) {
+ return openerp.qweb ? openerp.qweb.render('website.tour_popover_title', options) : options.title;
+ },
popover: function (options) {
- try {
- return openerp.qweb.render('website.tour_popover', options);
- } catch (e) {
- if (!this.automatic) throw e;
- return "";
- }
+ return openerp.qweb ? openerp.qweb.render('website.tour_popover', options) : options.title;
+ },
+ getLang: function () {
+ return $("html").attr("lang").replace(/-/, '_');
+ },
+ getState: function () {
+ var state = JSON.parse(localStorage.getItem("tour") || 'false') || {};
+ if (state) { this.time = state.time; }
+ var tour_id,mode,step_id;
+ if (!state.id && window.location.href.indexOf("#tutorial.") > -1) {
+ state = {
+ "id": window.location.href.match(/#tutorial\.(.*)=true/)[1],
+ "mode": "tutorial",
+ "step_id": 0
+ };
+ window.location.hash = "";
+ console.log("Tour Begin from url hash");
+ T.saveState(state.id, state.mode, state.step_id);
+ }
+ if (!state.id) {
+ return;
+ }
+ state.tour = T.tours[state.id];
+ state.step = state.tour && state.tour.steps[state.step_id === -1 ? 0 : state.step_id];
+ return state;
+ },
+ error: function (step, message) {
+ var state = T.getState();
+ message += '\n tour: ' + state.id
+ + '\n step: ' + step.id + ": '" + (step._title || step.title) + "'"
+ + '\n href: ' + window.location.href
+ + '\n referrer: ' + document.referrer
+ + '\n element: ' + Boolean(!step.element || ($(step.element).size() && $(step.element).is(":visible") && !$(step.element).is(":hidden")))
+ + '\n waitNot: ' + Boolean(!step.waitNot || !$(step.waitNot).size())
+ + '\n waitFor: ' + Boolean(!step.waitFor || $(step.waitFor).size())
+ + "\n localStorage: " + JSON.stringify(localStorage)
+ + '\n\n' + $("body").html();
+ T.reset();
+ throw new Error(message);
+ },
+ lists: function () {
+ var tour_ids = [];
+ for (var k in T.tours) {
+ tour_ids.push(k);
+ }
+ return tour_ids;
+ },
+ saveState: function (tour_id, mode, step_id) {
+ localStorage.setItem("tour", JSON.stringify({"id":tour_id, "mode":mode, "step_id":step_id || 0, "time": this.time}));
+ },
+ reset: function () {
+ var state = T.getState();
+ if (state) {
+ for (var k in state.tour.steps) {
+ state.tour.steps[k].busy = false;
+ }
+ }
+ localStorage.removeItem("tour");
+ clearTimeout(T.timer);
+ clearTimeout(T.testtimer);
+ T.closePopover();
+ },
+ running: function () {
+ function run () {
+ var state = T.getState();
+ if (!state) return;
+ if (state.tour) {
+ console.log("Tour '"+state.id+"' is running");
+ T.registerSteps(state.tour);
+ T.nextStep();
+ } else {
+ console.log("Tour '"+state.id+"' wait for running (tour undefined)");
+ setTimeout(T.running, state.mode === "test" ? T.defaultDelay : T.retryRunningDelay);
+ }
+ }
+ setTimeout(function () {
+ if ($.ajaxBusy) {
+ $(document).ajaxStop(run);
+ } else {
+ run();
+ }
+ },0);
},
-
- timer: null,
- testtimer: null,
check: function (step) {
return (step &&
(!step.element || ($(step.element).size() && $(step.element).is(":visible") && !$(step.element).is(":hidden"))) &&
(!step.waitNot || !$(step.waitNot).size()) &&
(!step.waitFor || $(step.waitFor).size()));
},
- waitNextStep: function (step, callback, overlaps) {
- var self = this;
+ waitNextStep: function () {
+ var state = T.getState();
var time = new Date().getTime();
var timer;
+ var next = state.tour.steps[state.step.id+1];
+ var overlaps = state.mode === "test" ? T.errorDelay : 0;
window.onbeforeunload = function () {
- clearTimeout(self.timer);
- clearTimeout(self.testtimer);
+ clearTimeout(T.timer);
+ clearTimeout(T.testtimer);
};
- // check popover activity
- $(".popover.tour button")
- .off()
- .on("click", function () {
- var help = $("#help-menu-button");
- var offset = help.offset();
- var left = (offset.left > 0) ? (offset.left + help.width()) : offset.left;
- var top = (help.height() > 0) ? (offset.top + help.height()) : offset.top;
-
- if ($(this).is("[data-role='next']") && step.element) {
- $(".popover.tour").remove();
- }
- if (step.busy) return;
- if (!$(this).is("[data-role='next']") || !step.element) {
- $('.popover.tour')
- .animate({
- left: left,
- top: top,
- width: '1px',
- height: '1px',
- opacity: 0
- }, 800,
- function(){
- $(".popover.tour").remove();
- clearTimeout(self.timer);
- step.busy = true;
- self.tour.end();
- self.endTour(callback);
- });
- }
- });
-
function checkNext () {
- clearTimeout(self.timer);
- if (step.busy) return;
- if (self.check(step)) {
- step.busy = true;
+ T.autoTogglePopover();
+
+ clearTimeout(T.timer);
+ if (T.check(next)) {
+ clearTimeout(T.currentTimer);
// use an other timeout for cke dom loading
setTimeout(function () {
- self.nextStep(step.stepId, callback, overlaps);
- }, self.defaultDelay);
+ T.nextStep(next);
+ }, T.defaultDelay);
} else if (!overlaps || new Date().getTime() - time < overlaps) {
- if (self.current.element) {
- var $popover = $(".popover.tour");
- if(!$(self.current.element).is(":visible")) {
- $popover.data("hide", true).fadeOut(300);
- } else if($popover.data("hide")) {
- $popover.data("hide", false).fadeIn(150);
- }
- }
- self.timer = setTimeout(checkNext, self.defaultDelay);
+ T.timer = setTimeout(checkNext, T.defaultDelay);
} else {
- self.reset();
- throw new Error("Can't arrive to step " + step.stepId + ": '" + step._title + "'"
- + '\nhref: ' + window.location.href
- + '\nelement: ' + Boolean(!step.element || ($(step.element).size() && $(step.element).is(":visible") && !$(step.element).is(":hidden")))
- + '\nwaitNot: ' + Boolean(!step.waitNot || !$(step.waitNot).size())
- + '\nwaitFor: ' + Boolean(!step.waitFor || $(step.waitFor).size())
- + '\n\n' + $("body").html()
- );
+ T.error(next, "Can't reach the next step");
}
}
checkNext();
},
- step: function (stepId) {
- var steps = this.steps.slice(0,this.steps.length),
- step;
- while (step = steps.shift()) {
- if (!stepId || step.stepId === stepId)
- return step;
+ nextStep: function (step) {
+ var state = T.getState();
+
+ if (!state) {
+ return;
}
- return null;
- },
- next: function (stepId) {
- var steps = this.steps.slice(0,this.steps.length),
- step, next, index=0;
- while (step = steps.shift()) {
- if (!stepId || step.stepId === stepId) {
- // clear popover (fix for boostrap tour if the element is removed before destroy popover)
- $(".popover.tour").remove();
- // go to step in bootstrap tour
- this.tour.goto(index);
- if (step.onload) step.onload();
- next = steps.shift();
- break;
- }
- index++;
+
+ step = step || state.step;
+ T.saveState(state.id, state.mode, step.id);
+
+ if (step.id !== state.step_id) {
+ console.log("Tour Step: '" + (step._title || step.title) + "' (" + (new Date().getTime() - this.time) + "ms)");
}
- return next;
- },
- nextStep: function (stepId, callback, overlaps) {
- var self = this;
- if (!this.localStorage.getItem("tour-"+this.id+"-test")) return;
- this.localStorage.setItem("tour-"+this.id+"-test", stepId || 0);
+ T.autoTogglePopover(true);
- this.current = this.step(stepId);
- var next = this.next(stepId);
+ if (step.onload) {
+ step.onload();
+ }
+ var next = state.tour.steps[step.id+1];
if (next) {
setTimeout(function () {
- self.waitNextStep(next, callback, overlaps);
- if (callback) setTimeout(function(){callback.call(self, next);}, self.defaultDelay);
- }, next && next.wait || 0);
+ T.waitNextStep();
+ if (state.mode === "test") {
+ setTimeout(function(){
+ T.autoNextStep(state.tour, step);
+ }, T.defaultDelay);
+ }
+ }, next.wait || 0);
} else {
- this.endTour();
+ T.endTour();
}
},
endTour: function () {
- var test = parseInt(this.localStorage.getItem("tour-"+this.id+"-test"),10) >= this.steps.length-1;
- this.reset();
+ var state = T.getState();
+ var test = state.step.id >= state.tour.steps.length-1;
+ T.reset();
if (test) {
console.log('ok');
} else {
console.log('error');
}
},
- autoNextStep: function () {
- var self = this;
- clearTimeout(self.testtimer);
+ autoNextStep: function (tour, step) {
+ clearTimeout(T.testtimer);
function autoStep () {
- var step = self.current;
if (!step) return;
if (step.autoComplete) {
step.autoComplete(tour);
}
- var $popover = $(".popover.tour");
- if ($popover.find("button[data-role='next']:visible").size()) {
- $popover.find("button[data-role='next']:visible").click();
- $popover.remove();
- }
+ $(".popover.tour [data-role='next']").click();
var $element = $(step.element);
if (!$element.size()) return;
if (step.snippet) {
-
- var selector = '#oe_snippets '+step.snippet+' .oe_snippet_thumbnail';
- self.autoDragAndDropSnippet(selector);
-
- } else if (step.element.match(/#oe_snippets .* \.oe_snippet_thumbnail/)) {
-
- self.autoDragAndDropSnippet($element);
+
+ T.autoDragAndDropSnippet($element);
} else if ($element.is(":visible")) {
@@ -473,7 +509,7 @@ website.Tour = openerp.Class.extend({
}
}
- self.testtimer = setTimeout(autoStep, 100);
+ T.testtimer = setTimeout(autoStep, 100);
},
autoDragAndDropSnippet: function (selector) {
var $thumbnail = $(selector).first();
@@ -483,64 +519,11 @@ website.Tour = openerp.Class.extend({
var $dropZone = $(".oe_drop_zone").first();
var dropPosition = $dropZone.position();
$dropZone.trigger($.Event("mouseup", { which: 1, pageX: dropPosition.left, pageY: dropPosition.top }));
- },
-
-});
-
-
-website.Tour.tours = {};
-website.Tour.busy = false;
-website.Tour.add = function (tour) {
- website.Tour.waitReady(function () {
- tour = tour.id ? tour : new tour();
- if (!website.Tour.tours[tour.id]) {
- website.Tour.tours[tour.id] = tour;
- tour.running();
- }
- });
-};
-website.Tour.get = function (id) {
- return website.Tour.tours[id];
-};
-website.Tour.each = function (callback) {
- website.Tour.waitReady(function () {
- for (var k in website.Tour.tours) {
- callback.call(website.Tour.tours[k]);
- }
- });
-};
-website.Tour.waitReady = function (callback) {
- var self = this;
- $(document).ready(function () {
- if ($.ajaxBusy) {
- $(document).ajaxStop(function() {
- setTimeout(function () {
- callback.call(self);
- },0);
- });
- }
- else {
- setTimeout(function () {
- callback.call(self);
- },0);
- }
- });
-};
-website.Tour.run_test = function (id) {
- website.Tour.waitReady(function () {
- if (!website.Tour.is_busy()) {
- website.Tour.tours[id].run(true);
- }
- });
-};
-website.Tour.is_busy = function () {
- for (var k in this.localStorage) {
- if (!k.indexOf("tour-")) {
- return k;
- }
}
- return website.Tour.busy;
};
+//$(document).ready(T.running);
+website.ready().then(T.running);
+
}());
diff --git a/addons/website/tests/test_ui.py b/addons/website/tests/test_ui.py
index 54cd8a73a67..7eb88d00df3 100644
--- a/addons/website/tests/test_ui.py
+++ b/addons/website/tests/test_ui.py
@@ -8,6 +8,6 @@ class TestUi(openerp.tests.HttpCase):
self.phantom_js("/", "console.log('ok')", "openerp.website.editor", login='admin')
def test_04_admin_tour_banner(self):
- self.phantom_js("/", "openerp.website.Tour.run_test('banner')", "openerp.website.Tour.tours.banner", login='admin')
+ self.phantom_js("/", "openerp.website.Tour.run('banner', 'test')", "openerp.website.Tour.tours.banner", login='admin')
# vim:et:
diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml
index 324ae70f9e6..dc7ce76b8f5 100644
--- a/addons/website/views/website_templates.xml
+++ b/addons/website/views/website_templates.xml
@@ -256,7 +256,6 @@