From 05347eac9b8b116a3f844e21ba282180b6a4fa90 Mon Sep 17 00:00:00 2001 From: Mohammad Alhashash Date: Mon, 6 May 2013 22:04:20 +0200 Subject: [PATCH 001/356] Fixing bugs 1177076 and 1177063: Add module name in context view reference Add group stock.group_production_lot for Traceability section in view_move_form bzr revid: alhashash@centrivision.com-20130506200420-ph86vjd3d208ttly --- addons/stock/stock_view.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index 83362ab5d6f..5878a578868 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -776,7 +776,7 @@ - + @@ -911,7 +911,7 @@ - +
@@ -1038,7 +1038,7 @@ - +
@@ -1256,7 +1256,7 @@ + groups="stock.group_tracking_lot,stock.group_production_lot">
- -
-
-

Get Statistics On Your Purchases

-
-

-Get accurate statistics on the performance of your suppliers through flexible reporting: delivery delays, negotiated discount on prices, quantities purchased, etc. Integrate purchases with the analytic accounting to analyse your contracts profitability. -

-
-
- -
-
-
diff --git a/addons/purchase/static/description/purchase_dashboard.png b/addons/purchase/static/description/purchase_dashboard.png deleted file mode 100644 index 0939edea591abb5f5eead391e30c49418cb125b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77350 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 z 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 Ef)NYmW&i*H From 64a187d894c5492399878e3ac4ac9761a651900d Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Wed, 29 Jan 2014 18:26:15 +0530 Subject: [PATCH 024/356] [remove]procurement and stock:remove procurement and stock dashboard bzr revid: sunilsharma.sharma07@gmail.com-20140129125615-96m0pzvmdfi0m0yz --- addons/procurement/__openerp__.py | 1 - .../board_mrp_procurement_view.xml | 24 ------- addons/stock/__openerp__.py | 5 +- addons/stock/board_warehouse_view.xml | 67 ------------------- 4 files changed, 2 insertions(+), 95 deletions(-) delete mode 100644 addons/procurement/board_mrp_procurement_view.xml delete mode 100644 addons/stock/board_warehouse_view.xml diff --git a/addons/procurement/__openerp__.py b/addons/procurement/__openerp__.py index 48569fa76c4..d18d2b862df 100644 --- a/addons/procurement/__openerp__.py +++ b/addons/procurement/__openerp__.py @@ -55,7 +55,6 @@ depending on the product's configuration. 'procurement_workflow.xml', 'process/procurement_process.xml', 'company_view.xml', - 'board_mrp_procurement_view.xml', ], 'demo': ['stock_orderpoint.xml','procurement_demo.xml'], 'test': ['test/procurement.yml'], diff --git a/addons/procurement/board_mrp_procurement_view.xml b/addons/procurement/board_mrp_procurement_view.xml deleted file mode 100644 index bb1b5e62d82..00000000000 --- a/addons/procurement/board_mrp_procurement_view.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Procurement Exceptions - ir.actions.act_window - procurement.order - form - tree,form - [('state','=','exception')] - - - - board.mrp.procurement.form - board.board - - - - - - - - - diff --git a/addons/stock/__openerp__.py b/addons/stock/__openerp__.py index dd2a996f6dd..1d3aa096b73 100644 --- a/addons/stock/__openerp__.py +++ b/addons/stock/__openerp__.py @@ -28,8 +28,8 @@ Manage multi-warehouses, multi- and structured stock locations ============================================================== -The warehouse and inventory management is based on a hierarchical location structure, from warehouses to storage bins. -The double entry inventory system allows you to manage customers, suppliers as well as manufacturing inventories. +The warehouse and inventory management is based on a hierarchical location structure, from warehouses to storage bins. +The double entry inventory system allows you to manage customers, suppliers as well as manufacturing inventories. OpenERP has the capacity to manage lots and serial numbers ensuring compliance with the traceability requirements imposed by the majority of industries. @@ -86,7 +86,6 @@ Dashboard / Reports for Warehouse Management will include: 'partner_view.xml', 'report/report_stock_move_view.xml', 'report/report_stock_view.xml', - 'board_warehouse_view.xml', 'res_config_view.xml', ], 'test': [ diff --git a/addons/stock/board_warehouse_view.xml b/addons/stock/board_warehouse_view.xml deleted file mode 100644 index a1f327eab5d..00000000000 --- a/addons/stock/board_warehouse_view.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - report.stock.move.graph - report.stock.move - - - - - - - - - - - Incoming Products - report.stock.move - form - graph,tree - [('type','=','in'),('day','<=', time.strftime('%Y-%m-%d')),('day','>',(context_today()-datetime.timedelta(days=15)).strftime('%Y-%m-%d'))] - - {'search_default_in':1} - - - Outgoing Products - report.stock.move - form - graph,tree - [('type','=','out'),('day','<=', time.strftime('%Y-%m-%d')),('day','>',(context_today()-datetime.timedelta(days=15)).strftime('%Y-%m-%d'))] - - {'search_default_out':1} - - - board.warehouse.form - board.board - -
- - - - - - - - -
-
-
- - - Warehouse - board.board - form - form - - - - - -
-
From 9328ac99082109ef98628ecfff31f00be6db6ac7 Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Wed, 29 Jan 2014 18:43:53 +0530 Subject: [PATCH 025/356] [imp]:remove sapece bzr revid: sunilsharma.sharma07@gmail.com-20140129131353-26259ntx22t8wd4z --- addons/stock/__openerp__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/stock/__openerp__.py b/addons/stock/__openerp__.py index 1d3aa096b73..343de1bb4ec 100644 --- a/addons/stock/__openerp__.py +++ b/addons/stock/__openerp__.py @@ -28,8 +28,8 @@ Manage multi-warehouses, multi- and structured stock locations ============================================================== -The warehouse and inventory management is based on a hierarchical location structure, from warehouses to storage bins. -The double entry inventory system allows you to manage customers, suppliers as well as manufacturing inventories. +The warehouse and inventory management is based on a hierarchical location structure, from warehouses to storage bins. +The double entry inventory system allows you to manage customers, suppliers as well as manufacturing inventories. OpenERP has the capacity to manage lots and serial numbers ensuring compliance with the traceability requirements imposed by the majority of industries. From 9a30e69787486c74a4aef8144f278808a20eb799 Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Wed, 29 Jan 2014 18:54:37 +0530 Subject: [PATCH 026/356] [remove]:stock:remove stock dashboard description and image bzr revid: sunilsharma.sharma07@gmail.com-20140129132437-7r4id6sohcab0z9v --- addons/stock/__openerp__.py | 2 +- addons/stock/static/description/index.html | 17 ----------------- .../static/description/stock_reporting.png | Bin 25497 -> 0 bytes 3 files changed, 1 insertion(+), 18 deletions(-) delete mode 100644 addons/stock/static/description/stock_reporting.png diff --git a/addons/stock/__openerp__.py b/addons/stock/__openerp__.py index 343de1bb4ec..d2a1a77886a 100644 --- a/addons/stock/__openerp__.py +++ b/addons/stock/__openerp__.py @@ -53,7 +53,7 @@ Dashboard / Reports for Warehouse Management will include: * Moves Analysis """, 'website': 'http://www.openerp.com', - 'images': ['images/stock_forecast_report.png', 'images/delivery_orders.jpeg', 'images/inventory_analysis.jpeg','images/location.jpeg','images/moves_analysis.jpeg','images/physical_inventories.jpeg','images/warehouse_dashboard.jpeg'], + 'images': ['images/stock_forecast_report.png', 'images/delivery_orders.jpeg', 'images/inventory_analysis.jpeg','images/location.jpeg','images/moves_analysis.jpeg','images/physical_inventories.jpeg'], 'depends': ['product', 'account'], 'category': 'Warehouse Management', 'sequence': 16, diff --git a/addons/stock/static/description/index.html b/addons/stock/static/description/index.html index dd7fbe568f7..8b5b3245653 100644 --- a/addons/stock/static/description/index.html +++ b/addons/stock/static/description/index.html @@ -185,23 +185,6 @@ based on a hierarchical location structure.
-
-
-

Reporting and Dashboards

-

Analyse your warehouse efficiency to improve performance

-
-

-Get the insights you need to make smarter decisions. Design custom dashboards -to get a picture of your warehouse efficiency at a glance. Dig deeper with -real-time reports that anyone can create and share. -

-
-
- -
-
-
-
diff --git a/addons/stock/static/description/stock_reporting.png b/addons/stock/static/description/stock_reporting.png deleted file mode 100644 index 1ec54d18813e6d04ff6b7b46363e46f9caf627ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25497 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 From 685121bea3758ebd4c2d5ef413f7877f4a07bd49 Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Wed, 29 Jan 2014 19:01:15 +0530 Subject: [PATCH 027/356] [rem]:sale and crm :remove image bzr revid: sunilsharma.sharma07@gmail.com-20140129133115-mqozdx23ctzbmct5 --- addons/crm/__openerp__.py | 1 - addons/sale/__openerp__.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py index ba05cc1fb16..3c87e36e389 100644 --- a/addons/crm/__openerp__.py +++ b/addons/crm/__openerp__.py @@ -128,7 +128,6 @@ Dashboard for CRM will include: 'application': True, 'auto_install': False, 'images': [ - 'images/crm_dashboard.png', 'images/customers.png', 'images/leads.png', 'images/opportunities_kanban.png', diff --git a/addons/sale/__openerp__.py b/addons/sale/__openerp__.py index 8c5e506047e..1d698c17163 100644 --- a/addons/sale/__openerp__.py +++ b/addons/sale/__openerp__.py @@ -58,7 +58,7 @@ The Dashboard for the Sales Manager will include """, 'author': 'OpenERP SA', 'website': 'http://www.openerp.com', - 'images': ['images/sale_dashboard.jpeg','images/Sale_order_line_to_invoice.jpeg','images/sale_order.jpeg','images/sales_analysis.jpeg'], + 'images': ['images/Sale_order_line_to_invoice.jpeg','images/sale_order.jpeg','images/sales_analysis.jpeg'], 'depends': ['account_voucher'], 'data': [ 'wizard/sale_make_invoice_advance.xml', From 30170062ddcb294b38cbe2dcdd0eb69f768b43d1 Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Thu, 30 Jan 2014 10:16:32 +0530 Subject: [PATCH 028/356] [rem]:mrp:remove mrp dashboard bzr revid: sunilsharma.sharma07@gmail.com-20140130044632-m0kw1a8ya466tlg3 --- addons/mrp/__openerp__.py | 3 +- addons/mrp/board_manufacturing_view.xml | 37 ------------------------- 2 files changed, 1 insertion(+), 39 deletions(-) delete mode 100644 addons/mrp/board_manufacturing_view.xml diff --git a/addons/mrp/__openerp__.py b/addons/mrp/__openerp__.py index 99afd01707a..765eae6da61 100644 --- a/addons/mrp/__openerp__.py +++ b/addons/mrp/__openerp__.py @@ -28,7 +28,7 @@ 'category': 'Manufacturing', 'sequence': 18, 'summary': 'Manufacturing Orders, Bill of Materials, Routing', - 'images': ['images/bill_of_materials.jpeg', 'images/manufacturing_order.jpeg', 'images/planning_manufacturing_order.jpeg', 'images/manufacturing_analysis.jpeg', 'images/production_dashboard.jpeg','images/routings.jpeg','images/work_centers.jpeg'], + 'images': ['images/bill_of_materials.jpeg', 'images/manufacturing_order.jpeg', 'images/planning_manufacturing_order.jpeg', 'images/manufacturing_analysis.jpeg','images/routings.jpeg','images/work_centers.jpeg'], 'depends': ['product','procurement', 'stock', 'resource', 'purchase','process'], 'description': """ Manage the Manufacturing process in OpenERP @@ -70,7 +70,6 @@ Dashboard / Reports for MRP will include: 'process/procurement_process.xml', 'report/mrp_report_view.xml', 'report/mrp_production_order_view.xml', - 'board_manufacturing_view.xml', 'res_config_view.xml', ], 'demo': ['mrp_demo.xml'], diff --git a/addons/mrp/board_manufacturing_view.xml b/addons/mrp/board_manufacturing_view.xml deleted file mode 100644 index 6bd07c2648b..00000000000 --- a/addons/mrp/board_manufacturing_view.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - board.mrp.manager.form - board.board - -
- - - - - - - - -
-
-
- - - Manufacturing - board.board - form - form - - - - - -
-
From 742c1b22d1459db2691e0471e11c366aa2916788 Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Thu, 30 Jan 2014 10:46:11 +0530 Subject: [PATCH 030/356] [rem]:project and project_issue:remove project and project_issue dashboard and images bzr revid: sunilsharma.sharma07@gmail.com-20140130051611-b0m9zioykhlbi8jh --- addons/project/__openerp__.py | 2 - addons/project/board_project_view.xml | 64 ------------------- addons/project_issue/__openerp__.py | 1 - .../board_project_issue_view.xml | 45 ------------- 4 files changed, 112 deletions(-) delete mode 100644 addons/project/board_project_view.xml delete mode 100644 addons/project_issue/board_project_issue_view.xml diff --git a/addons/project/__openerp__.py b/addons/project/__openerp__.py index f5ba24c41cb..4247aa6ea2c 100644 --- a/addons/project/__openerp__.py +++ b/addons/project/__openerp__.py @@ -29,7 +29,6 @@ 'summary': 'Projects, Tasks', 'images': [ 'images/gantt.png', - 'images/project_dashboard.jpeg', 'images/project_task_tree.jpeg', 'images/project_task.jpeg', 'images/project.jpeg', @@ -72,7 +71,6 @@ Dashboard / Reports for Project Management will include: 'res_partner_view.xml', 'report/project_report_view.xml', 'report/project_cumulative.xml', - 'board_project_view.xml', 'res_config_view.xml', ], 'demo': ['project_demo.xml'], diff --git a/addons/project/board_project_view.xml b/addons/project/board_project_view.xml deleted file mode 100644 index 3a14f7b6274..00000000000 --- a/addons/project/board_project_view.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - project.task.tree - project.task - - - - - - - - - - - - - - - - - My Tasks - project.task - form - tree,form - [('user_id', '=', uid), ('stage_id.fold', '!=', True)] - - - - - board.project.form - board.board - -
- - - - - - - - -
-
-
- - - Project - board.board - form - form - menu - - - - - -
-
diff --git a/addons/project_issue/__openerp__.py b/addons/project_issue/__openerp__.py index 146cff0a407..97b6f736f54 100644 --- a/addons/project_issue/__openerp__.py +++ b/addons/project_issue/__openerp__.py @@ -46,7 +46,6 @@ It allows the manager to quickly check the issues, assign them and decide on the 'report/project_issue_report_view.xml', 'security/project_issue_security.xml', 'security/ir.model.access.csv', - 'board_project_issue_view.xml', 'res_config_view.xml', 'project_issue_data.xml' ], diff --git a/addons/project_issue/board_project_issue_view.xml b/addons/project_issue/board_project_issue_view.xml deleted file mode 100644 index 6eb48f5572d..00000000000 --- a/addons/project_issue/board_project_issue_view.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - Project Issue Board Tree - project.issue - - - - - - - - - - - - - - - - - - - My Project Issues - project.issue - form - tree,form - [('user_id', '=', uid)] - - - - - board.my.project.issue.form.inherit - board.board - - - - - - - - - From 18e1f423ae65f77073afd78393684f149b508982 Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Thu, 30 Jan 2014 10:53:23 +0530 Subject: [PATCH 031/356] [rem]:event:remove event dashboard bzr revid: sunilsharma.sharma07@gmail.com-20140130052323-702ji8w2lowhyzer --- addons/event/__openerp__.py | 1 - addons/event/board_association_view.xml | 74 ------------------------- 2 files changed, 75 deletions(-) delete mode 100644 addons/event/board_association_view.xml diff --git a/addons/event/__openerp__.py b/addons/event/__openerp__.py index e8ecb09bf03..15cd863dc55 100644 --- a/addons/event/__openerp__.py +++ b/addons/event/__openerp__.py @@ -47,7 +47,6 @@ Key Features 'event_view.xml', 'event_data.xml', 'report/report_event_registration_view.xml', - 'board_association_view.xml', 'res_partner_view.xml', 'email_template.xml', ], diff --git a/addons/event/board_association_view.xml b/addons/event/board_association_view.xml deleted file mode 100644 index f48722bfea6..00000000000 --- a/addons/event/board_association_view.xml +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - Registration Event report - report.event.registration - - - - - - - - - - Events Filling Status - report.event.registration - form - [('event_state','not in',('cancel','done'))] - graph,tree - - - - - Next Events - ir.actions.act_window - event.event - form - tree,form - [('state','not in',('cancel','done'))] - - - - New Registration - ir.actions.act_window - event.registration - form - tree,form - [('state','=','draft')] - - - - board.associations.manager.form - board.board - -
- - - - - - - - -
-
-
- - - Events - board.board - form - form - - - - -
-
From 8ae9f5fa1d787a9e497f219066fa96dcd1b18bdc Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Thu, 30 Jan 2014 11:09:17 +0530 Subject: [PATCH 032/356] [rem]:account:remove account dashboard bzr revid: sunilsharma.sharma07@gmail.com-20140130053917-8n5b9vh7rf2k5cv4 --- addons/account/__openerp__.py | 1 - addons/account/board_account_view.xml | 46 --------------------------- 2 files changed, 47 deletions(-) delete mode 100644 addons/account/board_account_view.xml diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 343512dc271..e372c58c759 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -119,7 +119,6 @@ for a particular financial year and for preparation of vouchers there is a modul 'process/supplier_invoice_process.xml', 'ir_sequence_view.xml', 'company_view.xml', - 'board_account_view.xml', 'edi/invoice_action_data.xml', 'account_bank_view.xml', 'res_config_view.xml', diff --git a/addons/account/board_account_view.xml b/addons/account/board_account_view.xml deleted file mode 100644 index abbc81ade2b..00000000000 --- a/addons/account/board_account_view.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - Company Analysis - account.entries.report - form - tree,graph - {'group_by':['user_type'], 'group_by_no_leaf':1} - - [('year','=',time.strftime('%Y'))] - - - - board.account.form - board.board - -
- - - - - -
-
-
- - - Accounting - board.board - form - form - menu - - - - - -
-
From efe7f2ba00b9a845b83d34117fb8d023c2af9d3a Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Thu, 30 Jan 2014 11:20:32 +0530 Subject: [PATCH 033/356] [rem]:fleet:remove fleet dashboard bzr revid: sunilsharma.sharma07@gmail.com-20140130055032-79lxdzeqfrzda3l4 --- addons/fleet/__openerp__.py | 1 - addons/fleet/fleet_board_view.xml | 154 ------------------------------ 2 files changed, 155 deletions(-) delete mode 100644 addons/fleet/fleet_board_view.xml diff --git a/addons/fleet/__openerp__.py b/addons/fleet/__openerp__.py index 29c0e9d3ceb..3edf8ea8e0d 100644 --- a/addons/fleet/__openerp__.py +++ b/addons/fleet/__openerp__.py @@ -54,7 +54,6 @@ Main Features 'fleet_view.xml', 'fleet_cars.xml', 'fleet_data.xml', - 'fleet_board_view.xml', ], 'images': ['images/costs_analysis.jpeg','images/indicative_costs_analysis.jpeg','images/vehicles.jpeg','images/vehicles_contracts.jpeg','images/vehicles_fuel.jpeg','images/vehicles_odometer.jpeg','images/vehicles_services.jpeg'], diff --git a/addons/fleet/fleet_board_view.xml b/addons/fleet/fleet_board_view.xml deleted file mode 100644 index 41093d2c283..00000000000 --- a/addons/fleet/fleet_board_view.xml +++ /dev/null @@ -1,154 +0,0 @@ - - - - - Fuel Costs by Month - fleet.vehicle.cost - - form - tree - ['&',('parent_id','=',False),('cost_type','=','fuel')] - - - - Services Costs by Month - fleet.vehicle.cost - - form - tree - ['&',('parent_id','=',False),('cost_type','=','services')] - - - - Contracts Costs by Month - fleet.vehicle.cost - - form - tree - ['&',('parent_id','=',False),('cost_type','=','contract')] - - - - Costs by Month - fleet.vehicle.cost - - form - tree - [('parent_id','=',False)] - - - - Vehicles with alerts - fleet.vehicle - - form - tree - ['|',('contract_renewal_due_soon','=',True),('contract_renewal_overdue','=',True)] - -

- Here are displayed vehicles for which one or more contracts need to be renewed. If you see this message, then there is no contracts to renew. -

-
-
- - - Costs Analysis - fleet.vehicle.cost - - form - tree - {"search_default_parent_false" : True,"search_default_groupby_year" : True,"search_default_groupby_cost_type" : True,"search_default_groupby_cost_subtype" : True, "search_default_groupby_vehicle_id" : True,} - -

- OpenERP helps you managing the costs for your different vehicles - Costs are generally created from services and contract and appears here. -

-

- Thanks to the different filters, OpenERP can only print the effective - costs, sort them by type and by vehicle. -

-
-
- - - Indicative Costs Analysis - fleet.vehicle.cost - - form - tree - {"search_default_parent_true" : True,"search_default_groupby_cost_subtype" : True,"search_default_groupby_cost_type" : True,"search_default_groupby_parent_id" : True,} - -

- OpenERP helps you managing the costs for your different vehicles - Costs are generally created from services and contract and appears here. -

-

- Thanks to the different filters, OpenERP can only print the effective - costs, sort them by type and by vehicle. -

-
-
- - - board.fleet.form - board.board - -
- - - - - - - - - - - -
-
-
- - - Fleet - board.board - form - form - menu - - -
-

- Fleet dashboard is empty. -

- To add your first report into this dashboard, go to any - menu, switch to list or graph view, and click 'Add to - Dashboard' in the extended search options. -

- You can filter and group data before inserting into the - dashboard using the search options. -

-
-
-
- - - - - - - -
-
From a267d86b294315ef528e1a46465e5406827e8d62 Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Thu, 30 Jan 2014 12:19:23 +0530 Subject: [PATCH 034/356] [rem,imp]:hr:remove hr dashboard and improve hr view bzr revid: sunilsharma.sharma07@gmail.com-20140130064923-35tvo8q3sy4n09vx --- addons/hr/__openerp__.py | 1 - addons/hr/board_hr_view.xml | 49 ------------------- addons/hr/hr_view.xml | 1 + addons/hr_evaluation/__openerp__.py | 1 - .../board_hr_evaluation_view.xml | 26 ---------- addons/hr_expense/__openerp__.py | 1 - addons/hr_expense/board_hr_expense_view.xml | 26 ---------- addons/hr_holidays/__openerp__.py | 1 - addons/hr_holidays/board_hr_holidays_view.xml | 27 ---------- addons/hr_recruitment/__openerp__.py | 1 - .../board_hr_recruitment_statistical_view.xml | 41 ---------------- addons/hr_timesheet_sheet/__openerp__.py | 1 - .../board_hr_timesheet_view.xml | 37 -------------- 13 files changed, 1 insertion(+), 212 deletions(-) delete mode 100644 addons/hr/board_hr_view.xml delete mode 100644 addons/hr_evaluation/board_hr_evaluation_view.xml delete mode 100644 addons/hr_expense/board_hr_expense_view.xml delete mode 100644 addons/hr_holidays/board_hr_holidays_view.xml delete mode 100644 addons/hr_recruitment/board_hr_recruitment_statistical_view.xml delete mode 100644 addons/hr_timesheet_sheet/board_hr_timesheet_view.xml diff --git a/addons/hr/__openerp__.py b/addons/hr/__openerp__.py index ef7225788f6..b06497874d0 100644 --- a/addons/hr/__openerp__.py +++ b/addons/hr/__openerp__.py @@ -52,7 +52,6 @@ You can manage: 'data': [ 'security/hr_security.xml', 'security/ir.model.access.csv', - 'board_hr_view.xml', 'hr_view.xml', 'process/hr_process.xml', 'hr_installer.xml', diff --git a/addons/hr/board_hr_view.xml b/addons/hr/board_hr_view.xml deleted file mode 100644 index 84db6128f41..00000000000 --- a/addons/hr/board_hr_view.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - board.hr.form - board.board - -
- - - - -
-
-
- - - Human Resources - board.board - form - form - menu - - -
-

- Human Resources dashboard is empty. -

- To add your first report into this dashboard, go to any - menu, switch to list or graph view, and click 'Add to - Dashboard' in the extended search options. -

- You can filter and group data before inserting into the - dashboard using the search options. -

-
-
-
- - -
-
diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml index eea2eb2ce41..620593d20a9 100644 --- a/addons/hr/hr_view.xml +++ b/addons/hr/hr_view.xml @@ -7,6 +7,7 @@ id="menu_hr_root" groups="base.group_hr_manager,base.group_hr_user,base.group_user" sequence="90"/> + - - - - - Interview Requests - hr.evaluation.interview - form - - [('is_evaluation' ,'=', True), ('user_id', '=', uid),('state','=','waiting_answer')] - - - - - board.hr.evaluation.form - board.board - - - - - - - - - - diff --git a/addons/hr_expense/__openerp__.py b/addons/hr_expense/__openerp__.py index f149bc0c7c6..928d1827d95 100644 --- a/addons/hr_expense/__openerp__.py +++ b/addons/hr_expense/__openerp__.py @@ -57,7 +57,6 @@ This module also uses analytic accounting and is compatible with the invoice on 'process/hr_expense_process.xml', 'security/ir_rule.xml', 'report/hr_expense_report_view.xml', - 'board_hr_expense_view.xml', 'hr_expense_installer_view.xml', ], 'demo': ['hr_expense_demo.xml'], diff --git a/addons/hr_expense/board_hr_expense_view.xml b/addons/hr_expense/board_hr_expense_view.xml deleted file mode 100644 index 3f896a76fae..00000000000 --- a/addons/hr_expense/board_hr_expense_view.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - My Expenses - hr.expense.expense - form - [('state','in',('confirm', 'accepted')),('user_id','=',uid)] - {'default_user_id': uid} - - - - - board.hr.expense.form - board.board - - - - - - - - - - diff --git a/addons/hr_holidays/__openerp__.py b/addons/hr_holidays/__openerp__.py index 5d57dbab87e..eec73fe07e5 100644 --- a/addons/hr_holidays/__openerp__.py +++ b/addons/hr_holidays/__openerp__.py @@ -57,7 +57,6 @@ A synchronization with an internal agenda (Meetings of the CRM module) is also p 'report/available_holidays_view.xml', 'wizard/hr_holidays_summary_department_view.xml', 'wizard/hr_holidays_summary_employees_view.xml', - 'board_hr_holidays_view.xml', ], 'demo': ['hr_holidays_demo.xml',], 'js': ['static/src/js/*.js'], diff --git a/addons/hr_holidays/board_hr_holidays_view.xml b/addons/hr_holidays/board_hr_holidays_view.xml deleted file mode 100644 index c90ee32c396..00000000000 --- a/addons/hr_holidays/board_hr_holidays_view.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - My Leaves - hr.holidays.status - form - tree,form - - {} - [] - - - - board.hr.holidays.leave.month.form - board.board - - - - - - - - - - diff --git a/addons/hr_recruitment/__openerp__.py b/addons/hr_recruitment/__openerp__.py index cb1dd54e902..4697a9e6aac 100644 --- a/addons/hr_recruitment/__openerp__.py +++ b/addons/hr_recruitment/__openerp__.py @@ -52,7 +52,6 @@ You can define the different phases of interviews and easily rate the applicant 'security/hr_recruitment_security.xml', 'security/ir.model.access.csv', 'report/hr_recruitment_report_view.xml', - 'board_hr_recruitment_statistical_view.xml', 'hr_recruitment_installer_view.xml', 'res_config_view.xml', 'hr_recruitment_data.xml', diff --git a/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml b/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml deleted file mode 100644 index 2bec80858a5..00000000000 --- a/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - applicants.status.tree - hr.applicant - - - - - - - - - - - - - Applicants Status - ir.actions.act_window - hr.applicant - form - tree,form - [('stage_id.fold', '!=', True)] - - - - - board.hr.applicants.status.form - board.board - - - - - - - - - - diff --git a/addons/hr_timesheet_sheet/__openerp__.py b/addons/hr_timesheet_sheet/__openerp__.py index e92ab4400bd..0853e81690d 100644 --- a/addons/hr_timesheet_sheet/__openerp__.py +++ b/addons/hr_timesheet_sheet/__openerp__.py @@ -53,7 +53,6 @@ The validation can be configured in the company: 'hr_timesheet_sheet_view.xml', 'hr_timesheet_workflow.xml', 'process/hr_timesheet_sheet_process.xml', - 'board_hr_timesheet_view.xml', 'report/hr_timesheet_report_view.xml', 'report/timesheet_report_view.xml', 'wizard/hr_timesheet_current_view.xml', diff --git a/addons/hr_timesheet_sheet/board_hr_timesheet_view.xml b/addons/hr_timesheet_sheet/board_hr_timesheet_view.xml deleted file mode 100644 index 145d118ceb7..00000000000 --- a/addons/hr_timesheet_sheet/board_hr_timesheet_view.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - hr.timesheet.sheet.sheet.graph - hr_timesheet_sheet.sheet - - - - - - - - - - My Total Attendances By Week - hr_timesheet_sheet.sheet - form - graph - [('user_id','=',uid)] - - - - - board.hr.timesheet.sheet.form - board.board - - - - - - - - - - From bd31663a172d97c06f79a679d9fc814ec212f002 Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Thu, 30 Jan 2014 15:37:35 +0530 Subject: [PATCH 035/356] [imp]:fleet:improve fleet view bzr revid: sunilsharma.sharma07@gmail.com-20140130100735-pjs8eo6hhgp3i7zg --- addons/fleet/__openerp__.py | 1 + addons/fleet/fleet_board_view.xml | 57 +++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 addons/fleet/fleet_board_view.xml diff --git a/addons/fleet/__openerp__.py b/addons/fleet/__openerp__.py index 3edf8ea8e0d..29c0e9d3ceb 100644 --- a/addons/fleet/__openerp__.py +++ b/addons/fleet/__openerp__.py @@ -54,6 +54,7 @@ Main Features 'fleet_view.xml', 'fleet_cars.xml', 'fleet_data.xml', + 'fleet_board_view.xml', ], 'images': ['images/costs_analysis.jpeg','images/indicative_costs_analysis.jpeg','images/vehicles.jpeg','images/vehicles_contracts.jpeg','images/vehicles_fuel.jpeg','images/vehicles_odometer.jpeg','images/vehicles_services.jpeg'], diff --git a/addons/fleet/fleet_board_view.xml b/addons/fleet/fleet_board_view.xml new file mode 100644 index 00000000000..e657d904d64 --- /dev/null +++ b/addons/fleet/fleet_board_view.xml @@ -0,0 +1,57 @@ + + + + + + Costs Analysis + fleet.vehicle.cost + + form + tree + {"search_default_parent_false" : True,"search_default_groupby_year" : True,"search_default_groupby_cost_type" : True,"search_default_groupby_cost_subtype" : True, "search_default_groupby_vehicle_id" : True,} + +

+ OpenERP helps you managing the costs for your different vehicles + Costs are generally created from services and contract and appears here. +

+

+ Thanks to the different filters, OpenERP can only print the effective + costs, sort them by type and by vehicle. +

+
+
+ + + Indicative Costs Analysis + fleet.vehicle.cost + + form + tree + {"search_default_parent_true" : True,"search_default_groupby_cost_subtype" : True,"search_default_groupby_cost_type" : True,"search_default_groupby_parent_id" : True,} + +

+ OpenERP helps you managing the costs for your different vehicles + Costs are generally created from services and contract and appears here. +

+

+ Thanks to the different filters, OpenERP can only print the effective + costs, sort them by type and by vehicle. +

+
+
+ + + + + + +
+
From 16c05082c8c062b02b566acc4d1bc9ec85476e3e Mon Sep 17 00:00:00 2001 From: "Sunil Sharma (OpenERP)" Date: Tue, 4 Mar 2014 12:38:10 +0530 Subject: [PATCH 036/356] [remove]:mrp dashboard file bzr revid: sunilsharma.sharma07@gmail.com-20140304070810-282vhtpg3mduym0j --- addons/mrp/__openerp__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/mrp/__openerp__.py b/addons/mrp/__openerp__.py index 5b625486e9c..73985b7a4d8 100644 --- a/addons/mrp/__openerp__.py +++ b/addons/mrp/__openerp__.py @@ -69,7 +69,6 @@ Dashboard / Reports for MRP will include: 'process/service_product_process.xml', 'process/procurement_process.xml', 'report/mrp_report_view.xml', - 'board_manufacturing_view.xml', 'res_config_view.xml', 'views/report_mrporder.xml', From c2a92a5be2478960e18217dd8bbe951b0b74a2c8 Mon Sep 17 00:00:00 2001 From: "Randhir Mayatra (OpenERP)" Date: Tue, 4 Mar 2014 14:24:47 +0530 Subject: [PATCH 037/356] [IMP] remove menu_id from user model bzr revid: rajmyt@gmail.com-20140304085447-zknvpkfe4i3d8uku --- openerp/addons/base/base_data.xml | 1 - openerp/addons/base/res/res_users.py | 15 +-------------- openerp/addons/base/res/res_users_view.xml | 4 ---- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/openerp/addons/base/base_data.xml b/openerp/addons/base/base_data.xml index b6c12e111ff..96e13773cae 100644 --- a/openerp/addons/base/base_data.xml +++ b/openerp/addons/base/base_data.xml @@ -75,7 +75,6 @@ - -- Administrator diff --git a/openerp/addons/base/res/res_users.py b/openerp/addons/base/res/res_users.py index b2aba694078..fad3691a831 100644 --- a/openerp/addons/base/res/res_users.py +++ b/openerp/addons/base/res/res_users.py @@ -154,8 +154,6 @@ class res_users(osv.osv): "a change of password, the user has to login again."), 'signature': fields.text('Signature'), 'active': fields.boolean('Active'), - 'action_id': fields.many2one('ir.actions.actions', 'Home Action', help="If specified, this action will be opened at logon for this user, in addition to the standard menu."), - 'menu_id': fields.many2one('ir.actions.actions', 'Menu Action', help="If specified, the action will replace the standard menu for this user."), 'groups_id': fields.many2many('res.groups', 'res_groups_users_rel', 'uid', 'gid', 'Groups'), # Special behavior for this field: res.company.search() will only return the companies # available to the current user (should be the user's companies?), when the user_preference @@ -216,16 +214,6 @@ class res_users(osv.osv): return [c] return False - def _get_menu(self,cr, uid, context=None): - dataobj = self.pool.get('ir.model.data') - try: - model, res_id = dataobj.get_object_reference(cr, uid, 'base', 'action_menu_admin') - if model != 'ir.actions.act_window': - return False - return res_id - except ValueError: - return False - def _get_group(self,cr, uid, context=None): dataobj = self.pool.get('ir.model.data') result = [] @@ -243,7 +231,6 @@ class res_users(osv.osv): 'password': '', 'active': True, 'customer': False, - 'menu_id': _get_menu, 'company_id': _get_company, 'company_ids': _get_companies, 'groups_id': _get_group, @@ -251,7 +238,7 @@ class res_users(osv.osv): } # User can write on a few of his own fields (but not his groups for example) - SELF_WRITEABLE_FIELDS = ['password', 'signature', 'action_id', 'company_id', 'email', 'name', 'image', 'image_medium', 'image_small', 'lang', 'tz'] + SELF_WRITEABLE_FIELDS = ['password', 'signature', 'company_id', 'email', 'name', 'image', 'image_medium', 'image_small', 'lang', 'tz'] # User can read a few of his own fields SELF_READABLE_FIELDS = ['signature', 'company_id', 'login', 'email', 'name', 'image', 'image_medium', 'image_small', 'lang', 'tz', 'tz_offset', 'groups_id', 'partner_id', '__last_update'] diff --git a/openerp/addons/base/res/res_users_view.xml b/openerp/addons/base/res/res_users_view.xml index ba603c7a0f0..bb47f022a08 100644 --- a/openerp/addons/base/res/res_users_view.xml +++ b/openerp/addons/base/res/res_users_view.xml @@ -199,10 +199,6 @@ - - - - From 8c666af35c9c7b79373b999d1df2d4ba350e8f5d Mon Sep 17 00:00:00 2001 From: "Randhir Mayatra (OpenERP)" Date: Tue, 4 Mar 2014 14:26:59 +0530 Subject: [PATCH 038/356] [IMP] remove menu_id from user model bzr revid: rajmyt@gmail.com-20140304085659-lufot16ae59y1jca --- addons/web/controllers/main.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index a9f56a2099c..d1e450cc34f 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1054,16 +1054,7 @@ class Menu(http.Controller): """ s = request.session Menus = s.model('ir.ui.menu') - # If a menu action is defined use its domain to get the root menu items - user_menu_id = s.model('res.users').read([s.uid], ['menu_id'], - request.context)[0]['menu_id'] - menu_domain = [('parent_id', '=', False)] - if user_menu_id: - domain_string = s.model('ir.actions.act_window').read( - [user_menu_id[0]], ['domain'],request.context)[0]['domain'] - if domain_string: - menu_domain = ast.literal_eval(domain_string) return Menus.search(menu_domain, 0, False, False, request.context) From 9b74e8054eaafa2ec1a21d9e9291b24bdb81b2d2 Mon Sep 17 00:00:00 2001 From: "Randhir Mayatra (OpenERP)" Date: Tue, 4 Mar 2014 15:20:57 +0530 Subject: [PATCH 039/356] [IMP] remove fleet dashboard bzr revid: rajmyt@gmail.com-20140304095057-3i12fqxqz673z6i8 --- addons/fleet/fleet_board_view.xml | 99 ------------------------------- 1 file changed, 99 deletions(-) diff --git a/addons/fleet/fleet_board_view.xml b/addons/fleet/fleet_board_view.xml index c4732d9a0f4..9530ee0dd38 100644 --- a/addons/fleet/fleet_board_view.xml +++ b/addons/fleet/fleet_board_view.xml @@ -1,56 +1,6 @@ - - Fuel Costs by Month - fleet.vehicle.cost - - form - tree - ['&',('parent_id','=',False),('cost_type','=','fuel')] - - - - Services Costs by Month - fleet.vehicle.cost - - form - tree - ['&',('parent_id','=',False),('cost_type','=','services')] - - - - Contracts Costs by Month - fleet.vehicle.cost - - form - tree - ['&',('parent_id','=',False),('cost_type','=','contract')] - - - - Costs by Month - fleet.vehicle.cost - - form - tree - [('parent_id','=',False)] - - - - Vehicles with alerts - fleet.vehicle - - form - tree - ['|',('contract_renewal_due_soon','=',True),('contract_renewal_overdue','=',True)] - -

- Here are displayed vehicles for which one or more contracts need to be renewed. If you see this message, then there is no contracts to renew. -

-
-
- Costs Analysis fleet.vehicle.cost @@ -88,55 +38,6 @@

- - - board.fleet.form - board.board - -
- - - - - - - - - - - -
-
-
- - - Fleet - board.board - form - form - menu - - -
-

- Fleet dashboard is empty. -

- To add your first report into this dashboard, go to any - menu, switch to list or graph view, and click 'Add to - Dashboard' in the extended search options. -

- You can filter and group data before inserting into the - dashboard using the search options. -

-
-
-
- - From 84ed653322aa379ace94081dfde12ecc96bc485e Mon Sep 17 00:00:00 2001 From: "Randhir Mayatra (OpenERP)" Date: Tue, 4 Mar 2014 17:59:31 +0530 Subject: [PATCH 040/356] [IMP] remove tree view from task_hours_per_month report bzr revid: rajmyt@gmail.com-20140304122931-ektojfe9yvlxeiba --- .../report/task_report_view.xml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/addons/project_timesheet/report/task_report_view.xml b/addons/project_timesheet/report/task_report_view.xml index 2c8a34c656e..37d7b1486f9 100644 --- a/addons/project_timesheet/report/task_report_view.xml +++ b/addons/project_timesheet/report/task_report_view.xml @@ -9,20 +9,6 @@ - - report.timesheet.task.user.tree - report.timesheet.task.user - - - - - - - - - - - report.timesheet.task.user.search report.timesheet.task.user @@ -55,7 +41,7 @@ Task Hours Per Month report.timesheet.task.user form - tree,graph + graph {'search_default_year':1,'search_default_month':1, 'search_default_group_user_id':1} Date: Tue, 4 Mar 2014 18:37:27 +0530 Subject: [PATCH 041/356] [IMP] remove list view from member analysis report bzr revid: rajmyt@gmail.com-20140304130727-heez8ub1x4jq4dxw --- addons/membership/report/report_membership_view.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/membership/report/report_membership_view.xml b/addons/membership/report/report_membership_view.xml index 50b579d9ec2..100ec32043f 100644 --- a/addons/membership/report/report_membership_view.xml +++ b/addons/membership/report/report_membership_view.xml @@ -49,6 +49,7 @@ Members Analysis report.membership form + graph {"search_default_year":1,"search_default_member":1, 'search_default_Revenue':1, 'search_default_this_month':1, 'search_default_salesman':1,'group_by_no_leaf':1} From 60bdd78706336fa0221264f1675a8645bc2a99e9 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Thu, 20 Mar 2014 09:30:55 +0100 Subject: [PATCH 042/356] [IMP] website tour: refactoring and change api for create tour and test bzr revid: chm@openerp.com-20140320083055-z5ys4oey5brd439v --- .../static/src/js/website.tour.banner.js | 207 +++--- addons/website/static/src/js/website.tour.js | 689 +++++++----------- .../static/src/js/website.tour.blog.js | 210 +++--- .../static/src/js/website.tour.event.js | 206 +++--- .../static/src/js/website.tour.event_sale.js | 129 ++-- .../static/src/js/website.tour.sale.js | 159 ++-- .../static/src/js/website.tour.shop.js | 254 +++---- 7 files changed, 836 insertions(+), 1018 deletions(-) diff --git a/addons/website/static/src/js/website.tour.banner.js b/addons/website/static/src/js/website.tour.banner.js index 9c8afcd61e5..65099d03986 100644 --- a/addons/website/static/src/js/website.tour.banner.js +++ b/addons/website/static/src/js/website.tour.banner.js @@ -4,117 +4,106 @@ 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 centent 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 .carousel-caption > div', - placement: 'top', - title: _t("Customize banner's text"), - content: _t("Click in the text and start editing it."), - popover: { next: _t("Continue") }, - }, - { - 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 centent 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 .carousel-caption > div', + placement: 'top', + title: _t("Customize banner's text"), + content: _t("Click in the text and start editing it."), + popover: { next: _t("Continue") }, + }, + { + 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 b7e55fe166b..c5ccd1f42c9 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(); } @@ -36,27 +36,24 @@ function bootstrap_tour_stub () { } } - - if (website.EditorBar) { website.EditorBar.include({ tours: [], start: function () { var self = this; var menu = $('#help-menu'); - _.each(this.tours, function (tour) { + _.each(website.Tour.tours, function (tour) { + if (tour.mode != "tutorial") { + return; + } var $menuItem = $($.parseHTML('
  • '+tour.name+'
  • ')); $menuItem.click(function () { - tour.reset(); - tour.run(); + website.Tour.reset(); + website.Tour.run(tour.id); }); menu.append($menuItem); }); return this._super(); - }, - registerTour: function (tour) { - website.Tour.add(tour); - this.tours.push(tour); } }); } @@ -94,434 +91,312 @@ $.ajaxSetup({ } }); -website.Tour = openerp.Class.extend({ - steps: [], - defaultDelay: 50, //ms - defaultOverLaps: 5000, //ms - localStorage: window.localStorage, - init: function () {}, - run: function (automatic) { - this.reset(); +///////////////////////////////////////////////// - for (var k in this.localStorage) { - if (!k.indexOf("tour-") && k.indexOf("-test") > -1) return; + +var localStorage = window.localStorage; + +website.Tour = {}; +website.Tour.tours = {}; +website.Tour.state = null; +website.Tour.register = function (tour) { + website.Tour.tours[tour.id] = tour; +}; +website.Tour.run = function (tour_id, mode) { + if (localStorage.getItem("tour")) { // only one test running + return; + } + var tour = website.Tour.tours[tour_id]; + website.Tour.save_state(tour.id, mode || tour.mode, 0); + if (tour.path) { + window.location.href = "/"+website.Tour.getLang()+tour.path; + } +}; +website.Tour.registerSteps = function (tour) { + if (tour.register) { + return; + } + tour.register = true; + + 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:visible'; + } + if (!step.waitFor && index > 0 && tour.steps[index-1].snippet) { + step.waitFor = '.oe_overlay_options .oe_options:visible'; } - website.Tour.busy = true; + if (!step.element) step.orphan = true; - if (automatic) { - this.localStorage.setItem("tour-"+this.id+"-test-automatic", true); - } else { - this.localStorage.removeItem("tour-"+this.id+"-test-automatic"); + var snippet = 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'; } - this.automatic = automatic; + } + if (tour.steps[index-1] && + tour.steps[index-1].popover && tour.steps[index-1].popover.next) { + var step = { + step_id: index, + waitNot: '.popover.tour:visible' + }; + tour.steps.push(step); + } - 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(); - return; - } - - if (website.Tour.is_busy()) return; - - // launch tour with url - this.checkRunningUrl(); - - // mark tour as busy (only one test running) - if (this.localStorage.getItem("tour-"+this.id+"-test") != null) { - website.Tour.busy = true; - this.automatic = !!this.localStorage.getItem("tour-"+this.id+"-test-automatic"); - } - - if (!this.testPathUrl()) { - if (this.automatic) { - this.timer = setTimeout(function () { - self.reset(); - throw new Error("Wrong url for running " + self.id - + '\ntestPath: ' + self.testPath - + '\nhref: ' + window.location.href - + "\nreferrer: " + document.referrer - ); - },this.defaultOverLaps); - } - 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({ + // rendering bootstrap tour and popover + if (tour.mode != "test") { + tour.tour = new Tour({ name: this.id, - storage: this.tourStorage, + storage: localStorage, 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) { - step.waitFor = '.oe_overlay_options .oe_options:visible'; - } - + for (var index=0, len=tour.steps.length; index -1) { + tour_id = window.location.href.match(/#tutorial\.(.*)=true/)[1]; + mode = "tutorial"; + step_id = 0; + } + if (!tour_id) { + return; + } + var tour = website.Tour.tours[tour_id]; + return {'tour': tour, 'tour_id': tour_id, 'mode': mode, 'step_id': step_id}; +}; +website.Tour.error = function (tour, step, message) { + website.Tour.reset(); + throw new Error(message + + + "\ntour:" + tour.id + + + "\nstep:" + step.id + ": '" + (step._title || step.title) + "'" + + '\nhref: ' + window.location.href + + '\nreferrer: ' + document.referrer + + '\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() + ); +}; +website.Tour.lists = function () { + var tour_ids = []; + for (var k in website.Tour.tours) { + tour_ids.push(k); + } + return tour_ids; +}; +website.Tour.save_state = function (tour_id, mode, step_id) { + localStorage.setItem("tour", '{tour_id:'+tour_id+', mode:'+mode+', step_id:'+step_id+'}'); +}; +website.Tour.reset = function () { + var running = website.Tour.get_state(); + for (var k in running.tour.steps) { + running.tour.steps[k].busy = false; + } + localStorage.removeItem("tour"); + clearTimeout(website.Tour.timer); + clearTimeout(website.Tour.testtimer); - if (this.steps[index-1] && - this.steps[index-1].popover && this.steps[index-1].popover.next) { - var step = { - stepId: ""+index, - waitNot: '.popover.tour:visible' - }; - this.steps.push(step); - } + $('.popover.tour').remove(); +}; +website.Tour.running = function () { + var running = website.Tour.get_state(); + website.Tour.registerSteps(running.tour); + website.Tour.nextStep( running.tour, running.step_id ); +}; - this.tour.addSteps(this.steps); - }, +website.Tour.timer = null; +website.Tour.testtimer = null; +website.Tour.defaultDelay = 50; +website.Tour.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())); +}; +website.Tour.waitNextStep = function (tour, step, overlaps) { + var time = new Date().getTime(); + var timer; - popoverTitle: function (options) { - try { - return openerp.qweb.render('website.tour_popover_title', options); - } catch (e) { - if (!this.automatic) throw e; - return options.title; - } - }, - popover: function (options) { - try { - return openerp.qweb.render('website.tour_popover', options); - } catch (e) { - if (!this.automatic) throw e; - return ""; - } - }, + window.onbeforeunload = function () { + clearTimeout(website.Tour.timer); + clearTimeout(website.Tour.testtimer); + }; - 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; - var time = new Date().getTime(); - var timer; - - window.onbeforeunload = function () { - clearTimeout(self.timer); - clearTimeout(self.testtimer); - }; - - // check popover activity - $(".popover.tour button") - .off() - .on("click", function () { - $(".popover.tour").remove(); - if (step.busy) return; - if (!$(this).is("[data-role='next']")) { - clearTimeout(self.timer); - step.busy = true; - self.tour.end(); - self.endTour(callback); - } - }); - - function checkNext () { - clearTimeout(self.timer); + // check popover activity + $(".popover.tour button") + .off() + .on("click", function () { + $(".popover.tour").remove(); if (step.busy) return; - if (self.check(step)) { + if (!$(this).is("[data-role='next']")) { + clearTimeout(website.Tour.timer); step.busy = true; - // use an other timeout for cke dom loading - setTimeout(function () { - self.nextStep(step.stepId, callback, overlaps); - }, self.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); - } + if (tour.tour) { + tour.tour.end(); } - self.timer = setTimeout(checkNext, self.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() - ); + tour.endTour(tour); } - } - 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; - } - 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++; - } - 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); - - this.current = this.step(stepId); - var next = this.next(stepId); - - if (next) { + function checkNext () { + clearTimeout(website.Tour.timer); + if (step.busy) return; + if (website.Tour.check(step)) { + step.busy = true; + // use an other timeout for cke dom loading setTimeout(function () { - self.waitNextStep(next, callback, overlaps); - if (callback) setTimeout(function(){callback.call(self, next);}, self.defaultDelay); - }, next && next.wait || 0); - } else { - this.endTour(); - } - }, - endTour: function () { - var test = parseInt(this.localStorage.getItem("tour-"+this.id+"-test"),10) >= this.steps.length-1; - this.reset(); - if (test) { - console.log('ok'); - } else { - console.log('error'); - } - }, - autoNextStep: function () { - var self = this; - clearTimeout(self.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(); - } - - 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); - - } else if (step.sampleText) { - - $element.trigger($.Event("keydown", { srcElement: $element })); - if ($element.is("input") ) { - $element.val(step.sampleText); - } if ($element.is("select")) { - $element.find("[value='"+step.sampleText+"'], option:contains('"+step.sampleText+"')").attr("selected", true); - $element.val(step.sampleText); - } else { - $element.html(step.sampleText); + website.Tour.nextStep(tour, step, overlaps); + }, website.Tour.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); } - setTimeout(function () { - $element.trigger($.Event("keyup", { srcElement: $element })); - $element.trigger($.Event("change", { srcElement: $element })); - }, self.defaultDelay<<1); - - } else if ($element.is(":visible")) { - - $element.trigger($.Event("mouseenter", { srcElement: $element[0] })); - $element.trigger($.Event("mousedown", { srcElement: $element[0] })); - - var evt = document.createEvent("MouseEvents"); - evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); - $element[0].dispatchEvent(evt); - - // trigger after for step like: mouseenter, next step click on button display with mouseenter - setTimeout(function () { - $element.trigger($.Event("mouseup", { srcElement: $element[0] })); - $element.trigger($.Event("mouseleave", { srcElement: $element[0] })); - }, 1000); } - } - self.testtimer = setTimeout(autoStep, 100); - }, - autoDragAndDropSnippet: function (selector) { - var $thumbnail = $(selector).first(); - var thumbnailPosition = $thumbnail.position(); - $thumbnail.trigger($.Event("mousedown", { which: 1, pageX: thumbnailPosition.left, pageY: thumbnailPosition.top })); - $thumbnail.trigger($.Event("mousemove", { which: 1, pageX: document.body.scrollWidth/2, pageY: document.body.scrollHeight/2 })); - 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; + website.Tour.timer = setTimeout(checkNext, website.Tour.defaultDelay); + } else { + website.Tour.error(tour, step, "Can't arrive to the next step"); } } - return website.Tour.busy; + checkNext(); }; +website.Tour.nextStep = function (tour, step, overlaps) { + var state = website.Tour.get_state(); + website.Tour.save_state(tour.id, state.mode, step.id); + + // clear popover (fix for boostrap tour if the element is removed before destroy popover) + $(".popover.tour").remove(); + // go to step in bootstrap tour + tour.tour.goto(step.id); + step.onload(); + var next = tour.steps[step.id+1]; + + if (next) { + setTimeout(function () { + website.Tour.waitNextStep(tour, next, overlaps); + if (state.mode === "test") { + setTimeout(function(){ + website.Tour.autoNextStep(tour, step); + }, website.Tour.defaultDelay); + } + }, next && next.wait || 0); + } else { + website.Tour.endTour(tour); + } +}; +website.Tour.endTour = function (tour) { + var state = website.Tour.get_state(); + var test = state.step_id >= tour.steps.length-1; + this.reset(); + if (test) { + console.log('ok'); + } else { + console.log('error'); + } +}; +website.Tour.autoNextStep = function (tour, step) { + clearTimeout(website.Tour.testtimer); + + function autoStep () { + 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(); + } + + var $element = $(step.element); + if (!$element.size()) return; + + if (step.snippet) { + + website.Tour.autoDragAndDropSnippet($element); + + } else if (step.sampleText) { + + $element.trigger($.Event("keydown", { srcElement: $element })); + if ($element.is("input") ) { + $element.val(step.sampleText); + } if ($element.is("select")) { + $element.find("[value='"+step.sampleText+"'], option:contains('"+step.sampleText+"')").attr("selected", true); + $element.val(step.sampleText); + } else { + $element.html(step.sampleText); + } + setTimeout(function () { + $element.trigger($.Event("keyup", { srcElement: $element })); + $element.trigger($.Event("change", { srcElement: $element })); + }, website.Tour.defaultDelay<<1); + + } else if ($element.is(":visible")) { + + $element.trigger($.Event("mouseenter", { srcElement: $element[0] })); + $element.trigger($.Event("mousedown", { srcElement: $element[0] })); + + var evt = document.createEvent("MouseEvents"); + evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); + $element[0].dispatchEvent(evt); + + // trigger after for step like: mouseenter, next step click on button display with mouseenter + setTimeout(function () { + $element.trigger($.Event("mouseup", { srcElement: $element[0] })); + $element.trigger($.Event("mouseleave", { srcElement: $element[0] })); + }, 1000); + } + } + website.Tour.testtimer = setTimeout(autoStep, 100); +}; +website.Tour.autoDragAndDropSnippet = function (selector) { + var $thumbnail = $(selector).first(); + var thumbnailPosition = $thumbnail.position(); + $thumbnail.trigger($.Event("mousedown", { which: 1, pageX: thumbnailPosition.left, pageY: thumbnailPosition.top })); + $thumbnail.trigger($.Event("mousemove", { which: 1, pageX: document.body.scrollWidth/2, pageY: document.body.scrollHeight/2 })); + var $dropZone = $(".oe_drop_zone").first(); + var dropPosition = $dropZone.position(); + $dropZone.trigger($.Event("mouseup", { which: 1, pageX: dropPosition.left, pageY: dropPosition.top })); +}; + +website.ready(website.Tour.running); }()); diff --git a/addons/website_blog/static/src/js/website.tour.blog.js b/addons/website_blog/static/src/js/website.tour.blog.js index 3470e2e383b..050bf549225 100644 --- a/addons/website_blog/static/src/js/website.tour.blog.js +++ b/addons/website_blog/static/src/js/website.tour.blog.js @@ -4,117 +4,105 @@ var website = openerp.website; var _t = openerp._t; - website.EditorBar.include({ - start: function () { - this.registerTour(new website.Tour.Blog(this)); - return this._super(); - }, - }); - - website.Tour.Blog = website.Tour.extend({ - id: 'blog', - name: "Create a blog post", - testPath: '/(blog|blogpost)', - init: function () { - var self = this; - self.steps = [ - { - title: _t("New Blog Post"), - content: _t("Let's go through the first steps to write beautiful blog posts."), - popover: { next: _t("Start Tutorial"), end: _t("Skip") }, - }, - { - element: '#content-menu-button', - placement: 'left', - title: _t("Add Content"), - content: _t("Use this 'Content' menu to create a new blog post like any other document (page, menu, products, event, ...)."), - popover: { fixed: true }, - }, - { - element: 'a[data-action=new_blog_post]', - placement: 'left', - title: _t("New Blog Post"), - content: _t("Select this menu item to create a new blog post."), - popover: { fixed: true }, - }, - { - element: '.modal:has(#editor_new_blog) button.btn-primary', - placement: 'right', - title: _t("Create Blog Post"), - content: _t("Click Continue to create the blog post."), - }, - { - waitFor: 'body:has(button[data-action=save]:visible):has(.js_blog)', - title: _t("Blog Post Created"), - content: _t("This is your new blog post. Let's edit it."), - popover: { next: _t("Continue") }, - }, - { - element: 'h1[data-oe-expression="blog_post.name"]', - placement: 'bottom', - sampleText: 'New Blog', - title: _t("Set a Title"), - content: _t("Click on this area and set a catchy title for your blog post."), - }, - { - waitNot: '#wrap h1[data-oe-model="blog.post"]:contains("Blog Post Title")', - element: 'button[data-action=snippet]', - placement: 'left', - title: _t("Layout Your Blog Post"), - content: _t("Use well designed building blocks to structure the content of your blog. Click 'Insert Blocks' to add new content."), - popover: { fixed: true }, - }, - { - snippet: '#snippet_structure .oe_snippet:eq(2)', - placement: 'bottom', - title: _t("Drag & Drop a Block"), - content: _t("Drag this block and drop it in your page."), - popover: { fixed: true }, - }, - { - element: 'button[data-action=snippet]', - placement: 'bottom', - title: _t("Add Another Block"), - content: _t("Let's add another block to your post."), - popover: { fixed: true }, - }, - { - snippet: '#snippet_structure .oe_snippet:eq(4)', - placement: 'bottom', - title: _t("Drag & Drop a block"), - content: _t("Drag this block and drop it below the image block."), - popover: { fixed: true }, - }, - { - element: '.oe_active .oe_snippet_remove', - placement: 'top', - title: _t("Delete the block"), - content: _t("From this toolbar you can move, duplicate or delete the selected zone. Click on the garbage can image to delete the block. Or click on the Title and delete it."), - }, - { - waitNot: '.oe_active .oe_snippet_remove:visible', - element: 'button[data-action=save]', - placement: 'right', - title: _t("Save Your Blog"), - content: _t("Click the Save button to record changes on the page."), - popover: { fixed: true }, - }, - { - waitFor: 'button[data-action=edit]:visible', - element: 'button.btn-danger.js_publish_btn', - placement: 'top', - title: _t("Publish Your Post"), - content: _t("Your blog post is not yet published. You can update this draft version and publish it once you are ready."), - }, - { - waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible', - title: "Thanks!", - content: _t("This tutorial is finished. To discover more features, improve the content of this page and try the Promote button in the top right menu."), - popover: { next: _t("Close Tutorial") }, - }, - ]; - return this._super(); - }, + website.Tour.register({ + id: 'blog', + name: _t("Create a blog post"), + steps: [ + { + title: _t("New Blog Post"), + content: _t("Let's go through the first steps to write beautiful blog posts."), + popover: { next: _t("Start Tutorial"), end: _t("Skip") }, + }, + { + element: '#content-menu-button', + placement: 'left', + title: _t("Add Content"), + content: _t("Use this 'Content' menu to create a new blog post like any other document (page, menu, products, event, ...)."), + popover: { fixed: true }, + }, + { + element: 'a[data-action=new_blog_post]', + placement: 'left', + title: _t("New Blog Post"), + content: _t("Select this menu item to create a new blog post."), + popover: { fixed: true }, + }, + { + element: '.modal:has(#editor_new_blog) button.btn-primary', + placement: 'right', + title: _t("Create Blog Post"), + content: _t("Click Continue to create the blog post."), + }, + { + waitFor: 'body:has(button[data-action=save]:visible):has(.js_blog)', + title: _t("Blog Post Created"), + content: _t("This is your new blog post. Let's edit it."), + popover: { next: _t("Continue") }, + }, + { + element: 'h1[data-oe-expression="blog_post.name"]', + placement: 'bottom', + sampleText: 'New Blog', + title: _t("Set a Title"), + content: _t("Click on this area and set a catchy title for your blog post."), + }, + { + waitNot: '#wrap h1[data-oe-model="blog.post"]:contains("Blog Post Title")', + element: 'button[data-action=snippet]', + placement: 'left', + title: _t("Layout Your Blog Post"), + content: _t("Use well designed building blocks to structure the content of your blog. Click 'Insert Blocks' to add new content."), + popover: { fixed: true }, + }, + { + snippet: '#snippet_structure .oe_snippet:eq(2)', + placement: 'bottom', + title: _t("Drag & Drop a Block"), + content: _t("Drag this block and drop it in your page."), + popover: { fixed: true }, + }, + { + element: 'button[data-action=snippet]', + placement: 'bottom', + title: _t("Add Another Block"), + content: _t("Let's add another block to your post."), + popover: { fixed: true }, + }, + { + snippet: '#snippet_structure .oe_snippet:eq(4)', + placement: 'bottom', + title: _t("Drag & Drop a block"), + content: _t("Drag this block and drop it below the image block."), + popover: { fixed: true }, + }, + { + element: '.oe_active .oe_snippet_remove', + placement: 'top', + title: _t("Delete the block"), + content: _t("From this toolbar you can move, duplicate or delete the selected zone. Click on the garbage can image to delete the block. Or click on the Title and delete it."), + }, + { + waitNot: '.oe_active .oe_snippet_remove:visible', + element: 'button[data-action=save]', + placement: 'right', + title: _t("Save Your Blog"), + content: _t("Click the Save button to record changes on the page."), + popover: { fixed: true }, + }, + { + waitFor: 'button[data-action=edit]:visible', + element: 'button.btn-danger.js_publish_btn', + placement: 'top', + title: _t("Publish Your Post"), + content: _t("Your blog post is not yet published. You can update this draft version and publish it once you are ready."), + }, + { + waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible', + title: "Thanks!", + content: _t("This tutorial is finished. To discover more features, improve the content of this page and try the Promote button in the top right menu."), + popover: { next: _t("Close Tutorial") }, + }, + ] }); }()); diff --git a/addons/website_event/static/src/js/website.tour.event.js b/addons/website_event/static/src/js/website.tour.event.js index ef013398acb..07a12d81f34 100644 --- a/addons/website_event/static/src/js/website.tour.event.js +++ b/addons/website_event/static/src/js/website.tour.event.js @@ -4,115 +4,103 @@ var website = openerp.website; var _t = openerp._t; - website.EditorBar.include({ - start: function () { - this.registerTour(new website.EventTour(this)); - return this._super(); - }, - }); - - website.EventTour = website.Tour.extend({ - id: 'event', - name: "Create an event", - testPath: '/event(/[0-9]+/register)?', - init: function (editor) { - var self = this; - self.steps = [ - { - title: _t("Create an Event"), - content: _t("Let's go through the first steps to publish a new event."), - popover: { next: _t("Start Tutorial"), end: _t("Skip It") }, - }, - { - element: '#content-menu-button', - placement: 'left', - title: _t("Add Content"), - content: _t("The Content menu allows you to create new pages, events, menus, etc."), - popover: { fixed: true }, - }, - { - element: 'a[data-action=new_event]', - placement: 'left', - title: _t("New Event"), - content: _t("Click here to create a new event."), - popover: { fixed: true }, - }, - { - element: '.modal #editor_new_event input[type=text]', - sampleText: 'Advanced Technical Training', - placement: 'right', - title: _t("Create an Event Name"), - content: _t("Create a name for your new event and click 'Continue'. e.g: Technical Training"), - }, - { - waitNot: '.modal input[type=text]:not([value!=""])', - element: '.modal button.btn-primary', - placement: 'right', - title: _t("Create Event"), - content: _t("Click Continue to create the event."), - }, - { - waitFor: 'body:has(button[data-action=save]:visible):has(.js_event)', - title: _t("New Event Created"), - content: _t("This is your new event page. We will edit the event presentation page."), - popover: { next: _t("Continue") }, - }, - { - element: 'button[data-action=snippet]', - placement: 'bottom', - title: _t("Layout your event"), - content: _t("Insert blocks to layout the body of your event."), - popover: { fixed: true }, - }, - { - snippet: '#snippet_structure .oe_snippet:eq(2)', - placement: 'bottom', - title: _t("Drag & Drop a block"), - content: _t("Drag the 'Image-Text' block and drop it in your page."), - popover: { fixed: true }, - }, - { - - element: 'button[data-action=snippet]', - placement: 'bottom', - title: _t("Layout your event"), - content: _t("Insert another block to your event."), - popover: { fixed: true }, - }, - { - snippet: '#snippet_structure .oe_snippet:eq(4)', - placement: 'bottom', - title: _t("Drag & Drop a block"), - content: _t("Drag the 'Text Block' in your event page."), - popover: { fixed: true }, - }, - { - element: 'button[data-action=save]', - placement: 'right', - title: _t("Save your modifications"), - content: _t("Once you click on save, your event is updated."), - popover: { fixed: true }, - }, - { - waitFor: 'button[data-action=edit]:visible', - element: 'button.btn-danger.js_publish_btn', - placement: 'top', - title: _t("Publish your event"), - content: _t("Click to publish your event."), - }, - { - waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible', - element: '.js_publish_management button[data-toggle="dropdown"]', - placement: 'left', - title: _t("Customize your event"), - content: _t("Click here to customize your event further."), - }, - { - element: '.js_publish_management ul>li>a:last:visible', - }, - ]; - return this._super(); - } + website.Tour.register({ + id: 'event', + name: _t("Create an event"), + steps: [ + { + title: _t("Create an Event"), + content: _t("Let's go through the first steps to publish a new event."), + popover: { next: _t("Start Tutorial"), end: _t("Skip It") }, + }, + { + element: '#content-menu-button', + placement: 'left', + title: _t("Add Content"), + content: _t("The Content menu allows you to create new pages, events, menus, etc."), + popover: { fixed: true }, + }, + { + element: 'a[data-action=new_event]', + placement: 'left', + title: _t("New Event"), + content: _t("Click here to create a new event."), + popover: { fixed: true }, + }, + { + element: '.modal #editor_new_event input[type=text]', + sampleText: 'Advanced Technical Training', + placement: 'right', + title: _t("Create an Event Name"), + content: _t("Create a name for your new event and click 'Continue'. e.g: Technical Training"), + }, + { + waitNot: '.modal input[type=text]:not([value!=""])', + element: '.modal button.btn-primary', + placement: 'right', + title: _t("Create Event"), + content: _t("Click Continue to create the event."), + }, + { + waitFor: 'body:has(button[data-action=save]:visible):has(.js_event)', + title: _t("New Event Created"), + content: _t("This is your new event page. We will edit the event presentation page."), + popover: { next: _t("Continue") }, + }, + { + element: 'button[data-action=snippet]', + placement: 'bottom', + title: _t("Layout your event"), + content: _t("Insert blocks to layout the body of your event."), + popover: { fixed: true }, + }, + { + snippet: '#snippet_structure .oe_snippet:eq(2)', + placement: 'bottom', + title: _t("Drag & Drop a block"), + content: _t("Drag the 'Image-Text' block and drop it in your page."), + popover: { fixed: true }, + }, + { + + element: 'button[data-action=snippet]', + placement: 'bottom', + title: _t("Layout your event"), + content: _t("Insert another block to your event."), + popover: { fixed: true }, + }, + { + snippet: '#snippet_structure .oe_snippet:eq(4)', + placement: 'bottom', + title: _t("Drag & Drop a block"), + content: _t("Drag the 'Text Block' in your event page."), + popover: { fixed: true }, + }, + { + element: 'button[data-action=save]', + placement: 'right', + title: _t("Save your modifications"), + content: _t("Once you click on save, your event is updated."), + popover: { fixed: true }, + }, + { + waitFor: 'button[data-action=edit]:visible', + element: 'button.btn-danger.js_publish_btn', + placement: 'top', + title: _t("Publish your event"), + content: _t("Click to publish your event."), + }, + { + waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible', + element: '.js_publish_management button[data-toggle="dropdown"]', + placement: 'left', + title: _t("Customize your event"), + content: _t("Click here to customize your event further."), + }, + { + element: '.js_publish_management ul>li>a:last:visible', + }, + ] }); }()); diff --git a/addons/website_event_sale/static/src/js/website.tour.event_sale.js b/addons/website_event_sale/static/src/js/website.tour.event_sale.js index 48b9b62b349..92e57638cc8 100644 --- a/addons/website_event_sale/static/src/js/website.tour.event_sale.js +++ b/addons/website_event_sale/static/src/js/website.tour.event_sale.js @@ -3,77 +3,72 @@ var website = openerp.website; - website.Tour.EventSaleTest = website.Tour.extend({ - id: 'event_buy_tickets', + website.Tour.register({ + id: 'event_buy_tickets', name: "Try to buy tickets for event", path: '/event', - init: function () { - var self = this; - self.steps = [ - { - title: "select event", - element: 'a[href*="/event"]:contains("Open Days in Los Angeles")', + mode: 'test', + steps: [ + { + title: "select event", + element: 'a[href*="/event"]:contains("Open Days in Los Angeles")', + }, + { + title: "go to register page", + waitNot: 'a[href*="/event"]:contains("Functional Webinar")', + onload: function () { + // use onload if website_event_track is installed + if (!$('form:contains("Ticket Type")').size()) { + window.location.href = $('a[href*="/event"][href*="/register"]').attr("href"); + } }, - { - title: "go to register page", - waitNot: 'a[href*="/event"]:contains("Functional Webinar")', - onload: function () { - // use onload if website_event_track is installed - if (!$('form:contains("Ticket Type")').size()) { - window.location.href = $('a[href*="/event"][href*="/register"]').attr("href"); - } - }, + }, + { + title: "select 2 Standard tickets", + element: 'select[name="ticket-1"]', + sampleText: '2', + }, + { + title: "select 3 VIP tickets", + waitFor: 'select[name="ticket-1"] option:contains(2):selected', + element: 'select[name="ticket-2"]', + sampleText: '3', + }, + { + title: "Order Now", + waitFor: 'select[name="ticket-2"] option:contains(3):selected', + element: '.btn-primary:contains("Order Now")', + }, + { + title: "Complete checkout", + waitFor: '#top_menu .my_cart_quantity:contains(5)', + element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")', + onload: function (tour) { + if ($("input[name='name']").val() === "") + $("input[name='name']").val("website_sale-test-shoptest"); + if ($("input[name='email']").val() === "") + $("input[name='email']").val("website_event_sale_test_shoptest@websiteeventsaletest.optenerp.com"); + $("input[name='phone']").val("123"); + $("input[name='street']").val("123"); + $("input[name='city']").val("123"); + $("input[name='zip']").val("123"); + $("select[name='country_id']").val("21"); }, - { - title: "select 2 Standard tickets", - element: 'select[name="ticket-1"]', - sampleText: '2', - }, - { - title: "select 3 VIP tickets", - waitFor: 'select[name="ticket-1"] option:contains(2):selected', - element: 'select[name="ticket-2"]', - sampleText: '3', - }, - { - title: "Order Now", - waitFor: 'select[name="ticket-2"] option:contains(3):selected', - element: '.btn-primary:contains("Order Now")', - }, - { - title: "Complete checkout", - waitFor: '#top_menu .my_cart_quantity:contains(5)', - element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")', - onload: function (tour) { - if ($("input[name='name']").val() === "") - $("input[name='name']").val("website_sale-test-shoptest"); - if ($("input[name='email']").val() === "") - $("input[name='email']").val("website_event_sale_test_shoptest@websiteeventsaletest.optenerp.com"); - $("input[name='phone']").val("123"); - $("input[name='street']").val("123"); - $("input[name='city']").val("123"); - $("input[name='zip']").val("123"); - $("select[name='country_id']").val("21"); - }, - }, - { - title: "select payment", - element: '#payment_method label:has(img[title="transfer"]) input', - }, - { - title: "Pay Now", - waitFor: '#payment_method label:has(input:checked):has(img[title="transfer"])', - element: '.oe_sale_acquirer_button .btn[name="submit"]:visible', - }, - { - title: "finish", - waitFor: '.oe_website_sale:contains("Thank you for your order")', - } - ]; - return this._super(); - }, + }, + { + title: "select payment", + element: '#payment_method label:has(img[title="transfer"]) input', + }, + { + title: "Pay Now", + waitFor: '#payment_method label:has(input:checked):has(img[title="transfer"])', + element: '.oe_sale_acquirer_button .btn[name="submit"]:visible', + }, + { + title: "finish", + waitFor: '.oe_website_sale:contains("Thank you for your order")', + } + ] }); - // for test without editor bar - website.Tour.add(website.Tour.EventSaleTest); }()); diff --git a/addons/website_sale/static/src/js/website.tour.sale.js b/addons/website_sale/static/src/js/website.tour.sale.js index c9dd7e5e301..c7526b2468f 100644 --- a/addons/website_sale/static/src/js/website.tour.sale.js +++ b/addons/website_sale/static/src/js/website.tour.sale.js @@ -3,92 +3,87 @@ var website = openerp.website; - website.Tour.ShopTest = website.Tour.extend({ - id: 'shop_buy_product', + website.Tour.register({ + id: 'shop_buy_product', name: "Try to buy products", path: '/shop', - init: function () { - var self = this; - self.steps = [ - { - title: "select ipod", - element: '.oe_product_cart a:contains("iPod")', + mode: 'test', + steps: [ + { + title: "select ipod", + element: '.oe_product_cart a:contains("iPod")', + }, + { + title: "select ipod 32Go", + element: 'input[name="product_id"]:not([checked])', + }, + { + title: "click on add to cart", + waitFor: 'input[name="product_id"]:eq(1)[checked]', + element: 'form[action="/shop/add_cart"] .btn', + }, + { + title: "add suggested", + element: 'form[action="/shop/add_cart"] .btn-link:contains("Add to Cart")', + }, + { + title: "add one more iPod", + waitFor: '.my_cart_quantity:contains(2)', + element: '#mycart_products tr:contains("iPod: 32 Gb") a.js_add_cart_json:eq(1)', + }, + { + title: "remove Headphones", + waitFor: '#mycart_products tr:contains("iPod: 32 Gb") input.js_quantity[value=2]', + element: '#mycart_products tr:contains("Apple In-Ear Headphones") a.js_add_cart_json:first', + }, + { + title: "set one iPod", + waitNot: '#mycart_products tr:contains("Apple In-Ear Headphones")', + element: '#mycart_products input.js_quantity', + sampleText: '1', + }, + { + title: "go to checkout", + waitFor: '#mycart_products input.js_quantity[value=1]', + element: 'a[href="/shop/checkout"]', + }, + { + title: "test with input error", + element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")', + onload: function (tour) { + $("input[name='phone']").val(""); }, - { - title: "select ipod 32Go", - element: 'input[name="product_id"]:not([checked])', + }, + { + title: "test without input error", + waitFor: 'form[action="/shop/confirm_order"] .has-error', + element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")', + onload: function (tour) { + if ($("input[name='name']").val() === "") + $("input[name='name']").val("website_sale-test-shoptest"); + if ($("input[name='email']").val() === "") + $("input[name='email']").val("website_sale_test_shoptest@websitesaletest.optenerp.com"); + $("input[name='phone']").val("123"); + $("input[name='street']").val("123"); + $("input[name='city']").val("123"); + $("input[name='zip']").val("123"); + $("select[name='country_id']").val("21"); }, - { - title: "click on add to cart", - waitFor: 'input[name="product_id"]:eq(1)[checked]', - element: 'form[action="/shop/add_cart"] .btn', - }, - { - title: "add suggested", - element: 'form[action="/shop/add_cart"] .btn-link:contains("Add to Cart")', - }, - { - title: "add one more iPod", - waitFor: '.my_cart_quantity:contains(2)', - element: '#mycart_products tr:contains("iPod: 32 Gb") a.js_add_cart_json:eq(1)', - }, - { - title: "remove Headphones", - waitFor: '#mycart_products tr:contains("iPod: 32 Gb") input.js_quantity[value=2]', - element: '#mycart_products tr:contains("Apple In-Ear Headphones") a.js_add_cart_json:first', - }, - { - title: "set one iPod", - waitNot: '#mycart_products tr:contains("Apple In-Ear Headphones")', - element: '#mycart_products input.js_quantity', - sampleText: '1', - }, - { - title: "go to checkout", - waitFor: '#mycart_products input.js_quantity[value=1]', - element: 'a[href="/shop/checkout"]', - }, - { - title: "test with input error", - element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")', - onload: function (tour) { - $("input[name='phone']").val(""); - }, - }, - { - title: "test without input error", - waitFor: 'form[action="/shop/confirm_order"] .has-error', - element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")', - onload: function (tour) { - if ($("input[name='name']").val() === "") - $("input[name='name']").val("website_sale-test-shoptest"); - if ($("input[name='email']").val() === "") - $("input[name='email']").val("website_sale_test_shoptest@websitesaletest.optenerp.com"); - $("input[name='phone']").val("123"); - $("input[name='street']").val("123"); - $("input[name='city']").val("123"); - $("input[name='zip']").val("123"); - $("select[name='country_id']").val("21"); - }, - }, - { - title: "select payment", - element: '#payment_method label:has(img[title="transfer"]) input', - }, - { - title: "Pay Now", - waitFor: '#payment_method label:has(input:checked):has(img[title="transfer"])', - element: '.oe_sale_acquirer_button .btn[name="submit"]:visible', - }, - { - title: "finish", - waitFor: '.oe_website_sale:contains("Thank you for your order")', - } - ]; - return this._super(); - }, + }, + { + title: "select payment", + element: '#payment_method label:has(img[title="transfer"]) input', + }, + { + title: "Pay Now", + waitFor: '#payment_method label:has(input:checked):has(img[title="transfer"])', + element: '.oe_sale_acquirer_button .btn[name="submit"]:visible', + }, + { + title: "finish", + waitFor: '.oe_website_sale:contains("Thank you for your order")', + } + ] }); - // for test without editor bar - website.Tour.add(website.Tour.ShopTest); }()); diff --git a/addons/website_sale/static/src/js/website.tour.shop.js b/addons/website_sale/static/src/js/website.tour.shop.js index adb4a9e556c..bd03523e054 100644 --- a/addons/website_sale/static/src/js/website.tour.shop.js +++ b/addons/website_sale/static/src/js/website.tour.shop.js @@ -4,141 +4,129 @@ var website = openerp.website; var _t = openerp._t; - website.EditorBar.include({ - start: function () { - this.registerTour(new website.Tour.Shop(this)); - return this._super(); - }, - }); - - website.Tour.Shop = website.Tour.extend({ + website.Tour.register({ id: 'shop', - name: "Create a product", - testPath: '/shop', - init: function () { - var self = this; - self.steps = [ - { - title: _t("Welcome to your shop"), - content: _t("You successfully installed the e-commerce. This guide will help you to create your product and promote your sales."), - popover: { next: _t("Start Tutorial"), end: _t("Skip It") }, - }, - { - element: '#content-menu-button', - placement: 'left', - title: _t("Create your first product"), - content: _t("Click here to add a new product."), - popover: { fixed: true }, - }, - { - element: 'a[data-action=new_product]', - placement: 'left', - title: _t("Create a new product"), - content: _t("Select 'New Product' to create it and manage its properties to boost your sales."), - popover: { fixed: true }, - }, - { - element: '.modal #editor_new_product input[type=text]', - sampleText: 'New Product', - placement: 'right', - title: _t("Choose name"), - content: _t("Enter a name for your new product then click 'Continue'."), - }, - { - waitNot: '.modal input[type=text]:not([value!=""])', - element: '.modal button.btn-primary', - placement: 'right', - title: _t("Create Product"), - content: _t("Click Continue to create the product."), - }, - { - waitFor: 'body:has(button[data-action=save]:visible):has(.js_sale)', - title: _t("New product created"), - content: _t("This page contains all the information related to the new product."), - popover: { next: _t("Continue") }, - }, - { - element: '.product_price .oe_currency_value', - sampleText: '20.50', - placement: 'left', - title: _t("Change the price"), - content: _t("Edit the price of this product by clicking on the amount."), - }, - { - waitNot: '.product_price .oe_currency_value:containsExact(1.00)', - element: '#wrap img.img:first', - placement: 'top', - title: _t("Update image"), - content: _t("Click here to set an image describing your product."), - }, - { - element: 'button.hover-edition-button:visible', - placement: 'top', - title: _t("Update image"), - content: _t("Click here to set an image describing your product."), - }, - { - wait: 500, - element: '.well a.pull-right', - placement: 'bottom', - title: _t("Select an Image"), - content: _t("Let's select an existing image."), - popover: { fixed: true }, - }, - { - element: 'img[alt=imac]', - placement: 'bottom', - title: _t("Select an Image"), - content: _t("Let's select an imac image."), - popover: { fixed: true }, - }, - { - waitNot: 'img[alt=imac]', - element: '.modal-content button.save', - placement: 'bottom', - title: _t("Select this Image"), - content: _t("Click to add the image to the product decsription."), - popover: { fixed: true }, - }, - { - waitNot: '.modal-content:visible', - element: 'button[data-action=snippet]', - placement: 'bottom', - title: _t("Describe the Product"), - content: _t("Insert blocks like text-image, or gallery to fully describe the product."), - popover: { fixed: true }, - }, - { - snippet: '#snippet_structure .oe_snippet:eq(7)', - placement: 'bottom', - title: _t("Drag & Drop a block"), - content: _t("Drag the 'Big Picture' block and drop it in your page."), - popover: { fixed: true }, - }, - { - element: 'button[data-action=save]', - placement: 'right', - title: _t("Save your modifications"), - content: _t("Once you click on save, your product is updated."), - popover: { fixed: true }, + name: _t("Create a product"), + steps: [ + { + title: _t("Welcome to your shop"), + content: _t("You successfully installed the e-commerce. This guide will help you to create your product and promote your sales."), + popover: { next: _t("Start Tutorial"), end: _t("Skip It") }, + }, + { + element: '#content-menu-button', + placement: 'left', + title: _t("Create your first product"), + content: _t("Click here to add a new product."), + popover: { fixed: true }, + }, + { + element: 'a[data-action=new_product]', + placement: 'left', + title: _t("Create a new product"), + content: _t("Select 'New Product' to create it and manage its properties to boost your sales."), + popover: { fixed: true }, + }, + { + element: '.modal #editor_new_product input[type=text]', + sampleText: 'New Product', + placement: 'right', + title: _t("Choose name"), + content: _t("Enter a name for your new product then click 'Continue'."), + }, + { + waitNot: '.modal input[type=text]:not([value!=""])', + element: '.modal button.btn-primary', + placement: 'right', + title: _t("Create Product"), + content: _t("Click Continue to create the product."), + }, + { + waitFor: 'body:has(button[data-action=save]:visible):has(.js_sale)', + title: _t("New product created"), + content: _t("This page contains all the information related to the new product."), + popover: { next: _t("Continue") }, + }, + { + element: '.product_price .oe_currency_value', + sampleText: '20.50', + placement: 'left', + title: _t("Change the price"), + content: _t("Edit the price of this product by clicking on the amount."), + }, + { + waitNot: '.product_price .oe_currency_value:containsExact(1.00)', + element: '#wrap img.img:first', + placement: 'top', + title: _t("Update image"), + content: _t("Click here to set an image describing your product."), + }, + { + element: 'button.hover-edition-button:visible', + placement: 'top', + title: _t("Update image"), + content: _t("Click here to set an image describing your product."), + }, + { + wait: 500, + element: '.well a.pull-right', + placement: 'bottom', + title: _t("Select an Image"), + content: _t("Let's select an existing image."), + popover: { fixed: true }, + }, + { + element: 'img[alt=imac]', + placement: 'bottom', + title: _t("Select an Image"), + content: _t("Let's select an imac image."), + popover: { fixed: true }, + }, + { + waitNot: 'img[alt=imac]', + element: '.modal-content button.save', + placement: 'bottom', + title: _t("Select this Image"), + content: _t("Click to add the image to the product decsription."), + popover: { fixed: true }, + }, + { + waitNot: '.modal-content:visible', + element: 'button[data-action=snippet]', + placement: 'bottom', + title: _t("Describe the Product"), + content: _t("Insert blocks like text-image, or gallery to fully describe the product."), + popover: { fixed: true }, + }, + { + snippet: '#snippet_structure .oe_snippet:eq(7)', + placement: 'bottom', + title: _t("Drag & Drop a block"), + content: _t("Drag the 'Big Picture' block and drop it in your page."), + popover: { fixed: true }, + }, + { + element: 'button[data-action=save]', + placement: 'right', + title: _t("Save your modifications"), + content: _t("Once you click on save, your product is updated."), + popover: { fixed: true }, - }, - { - waitFor: '#website-top-navbar button[data-action="edit"]:visible', - element: '.js_publish_management button.js_publish_btn.btn-danger', - placement: 'top', - title: _t("Publish your product"), - content: _t("Click to publish your product so your customers can see it."), - }, - { - waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible', - title: _t("Congratulations"), - content: _t("Congratulations! You just created and published your first product."), - popover: { next: _t("Close Tutorial") }, - }, - ]; - return this._super(); - } + }, + { + waitFor: '#website-top-navbar button[data-action="edit"]:visible', + element: '.js_publish_management button.js_publish_btn.btn-danger', + placement: 'top', + title: _t("Publish your product"), + content: _t("Click to publish your product so your customers can see it."), + }, + { + waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible', + title: _t("Congratulations"), + content: _t("Congratulations! You just created and published your first product."), + popover: { next: _t("Close Tutorial") }, + }, + ] }); }()); From 0b75d4ea043e35c9c523b9ec24324eae8d149e8f Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Thu, 20 Mar 2014 09:56:06 +0100 Subject: [PATCH 043/356] [IMP] website: remove bootstrap_tour_stub bzr revid: chm@openerp.com-20140320085606-qrghlkev6i9pu9bw --- addons/website/static/src/js/website.tour.js | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index c5ccd1f42c9..fc0dd513cf2 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -23,17 +23,6 @@ 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) { @@ -150,7 +139,7 @@ website.Tour.registerSteps = function (tour) { } // rendering bootstrap tour and popover - if (tour.mode != "test") { + if (tour.mode != "test" || typeof Tour !== "undefined") { tour.tour = new Tour({ name: this.id, storage: localStorage, @@ -206,6 +195,7 @@ website.Tour.error = function (tour, step, message) { + '\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()) + + "\nlocalStorage: " + localStorage.getItem("tour") + '\n\n' + $("body").html() ); }; @@ -302,7 +292,9 @@ website.Tour.nextStep = function (tour, step, overlaps) { // clear popover (fix for boostrap tour if the element is removed before destroy popover) $(".popover.tour").remove(); // go to step in bootstrap tour - tour.tour.goto(step.id); + if (tour.tour) { + tour.tour.goto(step.id); + } step.onload(); var next = tour.steps[step.id+1]; @@ -349,7 +341,7 @@ website.Tour.autoNextStep = function (tour, step) { if (!$element.size()) return; if (step.snippet) { - + website.Tour.autoDragAndDropSnippet($element); } else if (step.sampleText) { From abfccd8936da4f228919dcd3f59462c38d9efe41 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Thu, 20 Mar 2014 16:15:42 +0100 Subject: [PATCH 044/356] [WIP] website tour: bootstrap-tour - v0.9.1 bzr revid: chm@openerp.com-20140320151542-blfr1rs1j24xanh8 --- .../lib/bootstrap-tour/bootstrap-tour.css | 44 +- .../lib/bootstrap-tour/bootstrap-tour.js | 1057 ++++++++++------- .../static/src/js/website.tour.banner.js | 3 - addons/website/static/src/js/website.tour.js | 199 ++-- 4 files changed, 755 insertions(+), 548 deletions(-) mode change 100755 => 100644 addons/website/static/lib/bootstrap-tour/bootstrap-tour.css mode change 100755 => 100644 addons/website/static/lib/bootstrap-tour/bootstrap-tour.js diff --git a/addons/website/static/lib/bootstrap-tour/bootstrap-tour.css b/addons/website/static/lib/bootstrap-tour/bootstrap-tour.css old mode 100755 new mode 100644 index 72c6e8cbab7..5449158e7f4 --- a/addons/website/static/lib/bootstrap-tour/bootstrap-tour.css +++ b/addons/website/static/lib/bootstrap-tour/bootstrap-tour.css @@ -1,37 +1,59 @@ +/* =========================================================== +# bootstrap-tour - v0.9.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. +*/ .tour-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; - z-index: 1009; + z-index: 1100; background-color: #000; opacity: 0.8; } .tour-step-backdrop { position: relative; - z-index: 1011; + z-index: 1101; + background: inherit; } .tour-step-background { position: absolute; - z-index: 1010; - background: #fff; + z-index: 1100; + background: inherit; border-radius: 6px; } +.popover[class*="tour-"] { + z-index: 1100; +} .popover[class*="tour-"] .popover-navigation { padding: 9px 14px; } -.popover[class*="tour-"] .popover-navigation *[data-role=end] { +.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] { +.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 { +.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 { diff --git a/addons/website/static/lib/bootstrap-tour/bootstrap-tour.js b/addons/website/static/lib/bootstrap-tour/bootstrap-tour.js old mode 100755 new mode 100644 index 8b45d6da9ee..a0172c718c0 --- a/addons/website/static/lib/bootstrap-tour/bootstrap-tour.js +++ b/addons/website/static/lib/bootstrap-tour/bootstrap-tour.js @@ -1,5 +1,5 @@ /* =========================================================== -# bootstrap-tour - v0.6.1 +# bootstrap-tour - v0.9.1 # http://bootstraptour.com # ============================================================== # Copyright 2012-2013 Ulrich Sossou @@ -16,220 +16,233 @@ # 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: "

    ", - afterSetState: function(key, value) {}, - afterGetState: function(key, value) {}, - afterRemoveState: function(key) {}, - onStart: function(tour) {}, - onEnd: function(tour) {}, - onShow: function(tour) {}, - onShown: function(tour) {}, - onHide: function(tour) {}, - onHidden: function(tour) {}, - onNext: function(tour) {}, - onPrev: function(tour) {} - }, options); - this._steps = []; - this.setCurrentStep(); - this.backdrop = { - overlay: null, - $element: null, - $background: null - }; +(function($, window) { + var Tour, document; + document = window.document; + Tour = (function() { + function Tour(options) { + this._options = $.extend({ + name: "tour", + steps: [], + container: "body", + keyboard: true, + storage: window.localStorage, + debug: false, + backdrop: false, + redirect: true, + orphan: false, + duration: false, + basePath: "", + template: "

    ", + afterSetState: function(key, value) {}, + afterGetState: function(key, value) {}, + afterRemoveState: function(key) {}, + onStart: function(tour) {}, + onEnd: function(tour) {}, + onShow: function(tour) {}, + onShown: function(tour) {}, + onHide: function(tour) {}, + onHidden: function(tour) {}, + onNext: function(tour) {}, + onPrev: function(tour) {}, + onPause: function(tour, duration) {}, + onResume: function(tour, duration) {} + }, options); + this._force = false; + this._inited = false; + this.backdrop = { + overlay: null, + $element: null, + $background: null, + backgroundShown: false, + overlayElementShown: false + }; + this; + } + + Tour.prototype.addSteps = function(steps) { + var step, _i, _len; + for (_i = 0, _len = steps.length; _i < _len; _i++) { + step = steps[_i]; + this.addStep(step); } + return this; + }; - Tour.prototype.setState = function(key, value) { - var keyName; - if (this._options.storage) { - keyName = "" + this._options.name + "_" + key; - this._options.storage.setItem(keyName, value); - return this._options.afterSetState(keyName, value); - } else { - if (this._state == null) { - this._state = {}; - } - return this._state[key] = value; - } - }; + Tour.prototype.addStep = function(step) { + this._options.steps.push(step); + return this; + }; - Tour.prototype.removeState = function(key) { - var keyName; - if (this._options.storage) { - keyName = "" + this._options.name + "_" + key; - this._options.storage.removeItem(keyName); - return this._options.afterRemoveState(keyName); - } else { - if (this._state != null) { - return delete this._state[key]; - } - } - }; + Tour.prototype.getStep = function(i) { + if (this._options.steps[i] != null) { + return $.extend({ + id: "step-" + i, + path: "", + placement: "right", + title: "", + content: "

    ", + next: i === this._options.steps.length - 1 ? -1 : i + 1, + prev: i - 1, + animation: true, + container: this._options.container, + backdrop: this._options.backdrop, + redirect: this._options.redirect, + orphan: this._options.orphan, + duration: this._options.duration, + template: this._options.template, + onShow: this._options.onShow, + onShown: this._options.onShown, + onHide: this._options.onHide, + onHidden: this._options.onHidden, + onNext: this._options.onNext, + onPrev: this._options.onPrev, + onPause: this._options.onPause, + onResume: this._options.onResume + }, this._options.steps[i]); + } + }; - Tour.prototype.getState = function(key) { - var keyName, value; - if (this._options.storage) { - keyName = "" + this._options.name + "_" + key; - value = this._options.storage.getItem(keyName); - } else { - if (this._state != null) { - value = this._state[key]; - } - } - if (value === void 0 || value === "null") { - value = null; - } - this._options.afterGetState(key, value); - return value; - }; - - Tour.prototype.addSteps = function(steps) { - var step, _i, _len, _results; - _results = []; - for (_i = 0, _len = steps.length; _i < _len; _i++) { - step = steps[_i]; - _results.push(this.addStep(step)); - } - return _results; - }; - - Tour.prototype.addStep = function(step) { - return this._steps.push(step); - }; - - Tour.prototype.getStep = function(i) { - if (this._steps[i] != null) { - return $.extend({ - id: "step-" + i, - path: "", - placement: "right", - title: "", - content: "

    ", - next: i === this._steps.length - 1 ? -1 : i + 1, - prev: i - 1, - animation: true, - container: this._options.container, - backdrop: this._options.backdrop, - redirect: this._options.redirect, - orphan: this._options.orphan, - template: this._options.template, - onShow: this._options.onShow, - onShown: this._options.onShown, - onHide: this._options.onHide, - onHidden: this._options.onHidden, - onNext: this._options.onNext, - onPrev: this._options.onPrev - }, this._steps[i]); - } - }; - - Tour.prototype.start = function(force) { - var promise, - _this = this; - if (force == null) { - force = false; - } - if (this.ended() && !force) { - return this._debug("Tour ended, start prevented."); - } - $(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=next]").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=next]:not(.disabled)", function(e) { - e.preventDefault(); - return _this.next(); - }); - $(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=prev]").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=prev]:not(.disabled)", function(e) { - e.preventDefault(); - return _this.prev(); - }); - $(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=end]").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=end]", function(e) { - e.preventDefault(); - return _this.end(); - }); - this._onResize(function() { + Tour.prototype.init = function(force) { + this._force = force; + if (this.ended()) { + this._debug("Tour ended, init prevented."); + return this; + } + this.setCurrentStep(); + this._initMouseNavigation(); + this._initKeyboardNavigation(); + this._onResize((function(_this) { + return function() { return _this.showStep(_this._current); - }); - this._setupKeyboardNavigation(); + }; + })(this)); + if (this._current !== null) { + this.showStep(this._current); + } + this._inited = true; + return this; + }; + + Tour.prototype.start = function(force) { + var promise; + if (force == null) { + force = false; + } + if (!this._inited) { + this.init(force); + } + if (this._current === null) { promise = this._makePromise(this._options.onStart != null ? this._options.onStart(this) : void 0); - return this._callOnPromiseDone(promise, this.showStep, this._current); - }; + this._callOnPromiseDone(promise, this.showStep, 0); + } + return this; + }; - Tour.prototype.next = function() { - var promise; - if (this.ended()) { - return this._debug("Tour ended, next prevented."); - } - promise = this.hideStep(this._current); - return this._callOnPromiseDone(promise, this._showNextStep); - }; + Tour.prototype.next = function() { + var promise; + promise = this.hideStep(this._current); + return this._callOnPromiseDone(promise, this._showNextStep); + }; - Tour.prototype.prev = function() { - var promise; - if (this.ended()) { - return this._debug("Tour ended, prev prevented."); - } - promise = this.hideStep(this._current); - return this._callOnPromiseDone(promise, this._showPrevStep); - }; + Tour.prototype.prev = function() { + var promise; + promise = this.hideStep(this._current); + return this._callOnPromiseDone(promise, this._showPrevStep); + }; - Tour.prototype.goto = function(i) { - var promise; - if (this.ended()) { - return this._debug("Tour ended, goto prevented."); - } - promise = this.hideStep(this._current); - return this._callOnPromiseDone(promise, this.showStep, i); - }; + Tour.prototype.goTo = function(i) { + var promise; + promise = this.hideStep(this._current); + return this._callOnPromiseDone(promise, this.showStep, i); + }; - Tour.prototype.end = function() { - var endHelper, hidePromise, - _this = this; - endHelper = function(e) { + Tour.prototype.end = function() { + var endHelper, promise; + endHelper = (function(_this) { + return function(e) { $(document).off("click.tour-" + _this._options.name); $(document).off("keyup.tour-" + _this._options.name); $(window).off("resize.tour-" + _this._options.name); - _this.setState("end", "yes"); + _this._setState("end", "yes"); + _this._inited = false; + _this._force = false; + _this._clearTimer(); if (_this._options.onEnd != null) { return _this._options.onEnd(_this); } }; - hidePromise = this.hideStep(this._current); - return this._callOnPromiseDone(hidePromise, endHelper); - }; + })(this); + promise = this.hideStep(this._current); + return this._callOnPromiseDone(promise, endHelper); + }; - Tour.prototype.ended = function() { - return !!this.getState("end"); - }; + Tour.prototype.ended = function() { + return !this._force && !!this._getState("end"); + }; - Tour.prototype.restart = function() { - this.removeState("current_step"); - this.removeState("end"); - this.setCurrentStep(0); - return this.start(); - }; + Tour.prototype.restart = function() { + this._removeState("current_step"); + this._removeState("end"); + this.setCurrentStep(0); + return this.start(); + }; - Tour.prototype.hideStep = function(i) { - var hideStepHelper, promise, step, - _this = this; - step = this.getStep(i); - promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0); - hideStepHelper = function(e) { + Tour.prototype.pause = function() { + var step; + step = this.getStep(this._current); + if (!(step && step.duration)) { + return this; + } + this._paused = true; + this._duration -= new Date().getTime() - this._start; + window.clearTimeout(this._timer); + this._debug("Paused/Stopped step " + (this._current + 1) + " timer (" + this._duration + " remaining)."); + if (step.onPause != null) { + return step.onPause(this, this._duration); + } + }; + + Tour.prototype.resume = function() { + var step; + step = this.getStep(this._current); + if (!(step && step.duration)) { + return this; + } + this._paused = false; + this._start = new Date().getTime(); + this._duration = this._duration || step.duration; + this._timer = window.setTimeout((function(_this) { + return function() { + if (_this._isLast()) { + return _this.next(); + } else { + return _this.end(); + } + }; + })(this), this._duration); + this._debug("Started step " + (this._current + 1) + " timer with duration " + this._duration); + if ((step.onResume != null) && this._duration !== step.duration) { + return step.onResume(this, this._duration); + } + }; + + Tour.prototype.hideStep = function(i) { + var hideStepHelper, promise, step; + step = this.getStep(i); + if (!step) { + return; + } + this._clearTimer(); + promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0); + hideStepHelper = (function(_this) { + return function(e) { var $element; - $element = _this._isOrphan(step) ? $("body") : $(step.element); - $element.popover("destroy"); + $element = $(step.element); + if (!($element.data("bs.popover") || $element.data("popover"))) { + $element = $("body"); + } + $element.popover("destroy").removeClass("tour-" + _this._options.name + "-element tour-" + _this._options.name + "-" + i + "-element"); if (step.reflex) { $element.css("cursor", "").off("click.tour-" + _this._options.name); } @@ -240,23 +253,37 @@ return step.onHidden(_this); } }; - this._callOnPromiseDone(promise, hideStepHelper); - return promise; - }; + })(this); + this._callOnPromiseDone(promise, hideStepHelper); + return promise; + }; - Tour.prototype.showStep = function(i) { - var promise, showStepHelper, skipToPrevious, step, - _this = this; - step = this.getStep(i); - if (!step) { - return; - } - skipToPrevious = i < this._current; - promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0); - showStepHelper = function(e) { + Tour.prototype.showStep = function(i) { + var promise, showStepHelper, skipToPrevious, step; + if (this.ended()) { + this._debug("Tour ended, showStep prevented."); + return this; + } + step = this.getStep(i); + if (!step) { + return; + } + skipToPrevious = i < this._current; + promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0); + showStepHelper = (function(_this) { + return function(e) { var current_path, path; _this.setCurrentStep(i); - path = $.isFunction(step.path) ? step.path.call() : _this._options.basePath + step.path; + path = (function() { + switch ({}.toString.call(step.path)) { + case "[object Function]": + return step.path(); + case "[object String]": + return this._options.basePath + step.path; + default: + return step.path; + } + }).call(_this); current_path = [document.location.pathname, document.location.hash].join(""); if (_this._isRedirect(path, current_path)) { _this._redirect(step, path); @@ -277,283 +304,413 @@ if (step.backdrop) { _this._showBackdrop(!_this._isOrphan(step) ? step.element : void 0); } - _this._showPopover(step, i); - if (step.onShown != null) { - step.onShown(_this); + _this._scrollIntoView(step.element, function() { + if ((step.element != null) && step.backdrop) { + _this._showOverlayElement(step.element); + } + _this._showPopover(step, i); + if (step.onShown != null) { + step.onShown(_this); + } + return _this._debug("Step " + (_this._current + 1) + " of " + _this._options.steps.length); + }); + if (step.duration) { + return _this.resume(); } - return _this._debug("Step " + (_this._current + 1) + " of " + _this._steps.length); }; - return this._callOnPromiseDone(promise, showStepHelper); - }; + })(this); + this._callOnPromiseDone(promise, showStepHelper); + return promise; + }; - Tour.prototype.setCurrentStep = function(value) { - if (value != null) { - this._current = value; - return this.setState("current_step", value); - } else { - this._current = this.getState("current_step"); - return this._current = this._current === null ? 0 : parseInt(this._current, 10); + Tour.prototype.getCurrentStep = function() { + return this._current; + }; + + Tour.prototype.setCurrentStep = function(value) { + if (value != null) { + this._current = value; + this._setState("current_step", value); + } else { + this._current = this._getState("current_step"); + this._current = this._current === null ? null : parseInt(this._current, 10); + } + return this; + }; + + Tour.prototype._setState = function(key, value) { + var e, keyName; + if (this._options.storage) { + keyName = "" + this._options.name + "_" + key; + try { + this._options.storage.setItem(keyName, value); + } catch (_error) { + e = _error; + if (e.code === DOMException.QUOTA_EXCEEDED_ERR) { + this.debug("LocalStorage quota exceeded. State storage failed."); + } } - }; + return this._options.afterSetState(keyName, value); + } else { + if (this._state == null) { + this._state = {}; + } + return this._state[key] = value; + } + }; - Tour.prototype._showNextStep = function() { - var promise, showNextStepHelper, step, - _this = this; - step = this.getStep(this._current); - showNextStepHelper = function(e) { + Tour.prototype._removeState = function(key) { + var keyName; + if (this._options.storage) { + keyName = "" + this._options.name + "_" + key; + this._options.storage.removeItem(keyName); + return this._options.afterRemoveState(keyName); + } else { + if (this._state != null) { + return delete this._state[key]; + } + } + }; + + Tour.prototype._getState = function(key) { + var keyName, value; + if (this._options.storage) { + keyName = "" + this._options.name + "_" + key; + value = this._options.storage.getItem(keyName); + } else { + if (this._state != null) { + value = this._state[key]; + } + } + if (value === void 0 || value === "null") { + value = null; + } + this._options.afterGetState(key, value); + return value; + }; + + Tour.prototype._showNextStep = function() { + var promise, showNextStepHelper, step; + step = this.getStep(this._current); + showNextStepHelper = (function(_this) { + return function(e) { return _this.showStep(step.next); }; - promise = this._makePromise((step.onNext != null ? step.onNext(this) : void 0)); - return this._callOnPromiseDone(promise, showNextStepHelper); - }; + })(this); + promise = this._makePromise(step.onNext != null ? step.onNext(this) : void 0); + return this._callOnPromiseDone(promise, showNextStepHelper); + }; - Tour.prototype._showPrevStep = function() { - var promise, showPrevStepHelper, step, - _this = this; - step = this.getStep(this._current); - showPrevStepHelper = function(e) { + Tour.prototype._showPrevStep = function() { + var promise, showPrevStepHelper, step; + step = this.getStep(this._current); + showPrevStepHelper = (function(_this) { + return function(e) { return _this.showStep(step.prev); }; - promise = this._makePromise((step.onPrev != null ? step.onPrev(this) : void 0)); - return this._callOnPromiseDone(promise, showPrevStepHelper); - }; + })(this); + promise = this._makePromise(step.onPrev != null ? step.onPrev(this) : void 0); + return this._callOnPromiseDone(promise, showPrevStepHelper); + }; - Tour.prototype._debug = function(text) { - if (this._options.debug) { - return window.console.log("Bootstrap Tour '" + this._options.name + "' | " + text); - } - }; + Tour.prototype._debug = function(text) { + if (this._options.debug) { + return window.console.log("Bootstrap Tour '" + this._options.name + "' | " + text); + } + }; - Tour.prototype._isRedirect = function(path, currentPath) { - return (path != null) && path !== "" && path.replace(/\?.*$/, "").replace(/\/?$/, "") !== currentPath.replace(/\/?$/, ""); - }; + Tour.prototype._isRedirect = function(path, currentPath) { + return (path != null) && path !== "" && ((toString.call(path) === "[object RegExp]" && !path.test(currentPath)) || (toString.call(path) === "[object String]" && path.replace(/\?.*$/, "").replace(/\/?$/, "") !== currentPath.replace(/\/?$/, ""))); + }; - Tour.prototype._redirect = function(step, path) { - if ($.isFunction(step.redirect)) { - return step.redirect.call(this, path); - } else if (step.redirect === true) { - this._debug("Redirect to " + path); - return document.location.href = path; - } - }; + Tour.prototype._redirect = function(step, path) { + if ($.isFunction(step.redirect)) { + return step.redirect.call(this, path); + } else if (step.redirect === true) { + this._debug("Redirect to " + path); + return document.location.href = path; + } + }; - Tour.prototype._isOrphan = function(step) { - return (step.element == null) || !$(step.element).length || $(step.element).is(":hidden"); - }; + Tour.prototype._isOrphan = function(step) { + return (step.element == null) || !$(step.element).length || $(step.element).is(":hidden") && ($(step.element)[0].namespaceURI !== "http://www.w3.org/2000/svg"); + }; - Tour.prototype._showPopover = function(step, i) { - var $element, $navigation, $template, $tip, isOrphan, options, - _this = this; - options = $.extend({}, this._options); - $template = $.isFunction(step.template) ? $(step.template(i, step)) : $(step.template); - $navigation = $template.find(".popover-navigation"); - isOrphan = this._isOrphan(step); - if (isOrphan) { - step.element = "body"; - step.placement = "top"; - $template = $template.addClass("orphan"); - } - $element = $(step.element); - $template.addClass("tour-" + this._options.name); - if (step.options) { - $.extend(options, step.options); - } - if (step.reflex) { - $element.css("cursor", "pointer").on("click.tour-" + this._options.name, function(e) { - if (_this._current < _this._steps.length - 1) { + Tour.prototype._isLast = function() { + return this._current < this._options.steps.length - 1; + }; + + Tour.prototype._showPopover = function(step, i) { + var $element, $navigation, $template, $tip, isOrphan, options; + options = $.extend({}, this._options); + $template = $.isFunction(step.template) ? $(step.template(i, step)) : $(step.template); + $navigation = $template.find(".popover-navigation"); + isOrphan = this._isOrphan(step); + if (isOrphan) { + step.element = "body>*:last"; + step.placement = "top"; + $template = $template.addClass("orphan"); + } + $element = $(step.element); + $template.addClass("tour-" + this._options.name + " tour-" + this._options.name + "-" + i); + $element.addClass("tour-" + this._options.name + "-element tour-" + this._options.name + "-" + i + "-element"); + if (step.options) { + $.extend(options, step.options); + } + if (step.reflex) { + $element.css("cursor", "pointer").on("click.tour-" + this._options.name, (function(_this) { + return function() { + if (_this._isLast()) { return _this.next(); } else { return _this.end(); } - }); - } - if (step.prev < 0) { - $navigation.find("*[data-role=prev]").addClass("disabled"); - } - if (step.next < 0) { - $navigation.find("*[data-role=next]").addClass("disabled"); - } - step.template = $template.clone().wrap("
    ").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); - } - }; + }; + })(this)); + } + if (step.prev < 0) { + $navigation.find("[data-role='prev']").addClass("disabled"); + } + if (step.next < 0) { + $navigation.find("[data-role='next']").addClass("disabled"); + } + if (!step.duration) { + $navigation.find("[data-role='pause-resume']").remove(); + } + step.template = $template.clone().wrap("
    ").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._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; + 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"); } - offsetRight = $("html").outerWidth() - tipOffset.left - $tip.outerWidth(); - if (offsetRight < 0) { - tipOffset.left = tipOffset.left + offsetRight; + } else { + if (originalTop !== tipOffset.top) { + return this._replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, "top"); } - 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"); + } + }; + + 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(element, callback) { + var $element, $window, counter, offsetTop, scrollTop, windowHeight; + $element = $(element); + if (!$element.length) { + return callback(); + } + $window = $(window); + offsetTop = $element.offset().top; + windowHeight = $window.height(); + scrollTop = Math.max(0, offsetTop - (windowHeight / 2)); + this._debug("Scroll into view. ScrollTop: " + scrollTop + ". Element offset: " + offsetTop + ". Window height: " + windowHeight + "."); + counter = 0; + return $("body,html").stop(true, true).animate({ + scrollTop: Math.ceil(scrollTop) + }, (function(_this) { + return function() { + if (++counter === 2) { + callback(); + return _this._debug("Scroll into view. Animation end element offset: " + ($element.offset().top) + ". Window height: " + ($window.height()) + "."); } + }; + })(this)); + }; + + Tour.prototype._onResize = function(callback, timeout) { + return $(window).on("resize.tour-" + this._options.name, function() { + clearTimeout(timeout); + return timeout = setTimeout(callback, 100); + }); + }; + + Tour.prototype._initMouseNavigation = function() { + var _this; + _this = this; + return $(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']:not(.disabled)").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']:not(.disabled)").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']:not(.disabled)", (function(_this) { + return function(e) { + e.preventDefault(); + return _this.next(); + }; + })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']:not(.disabled)", (function(_this) { + return function(e) { + e.preventDefault(); + return _this.prev(); + }; + })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']", (function(_this) { + return function(e) { + e.preventDefault(); + return _this.end(); + }; + })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']", function(e) { + var $this; + e.preventDefault(); + $this = $(this); + $this.text(_this._paused ? $this.data("pause-text") : $this.data("resume-text")); + if (_this._paused) { + return _this.resume(); } else { - if (originalTop !== tipOffset.top) { - return this._replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, "top"); - } + return _this.pause(); } - }; + }); + }; - 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(); + Tour.prototype._initKeyboardNavigation = function() { + if (!this._options.keyboard) { + return; + } + return $(document).on("keyup.tour-" + this._options.name, (function(_this) { + return function(e) { + if (!e.which) { + return; + } + switch (e.which) { + case 39: + e.preventDefault(); + if (_this._isLast()) { + 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(); + } + }; + })(this)); + }; - Tour.prototype._makePromise = function(result) { - if (result && $.isFunction(result.then)) { - return result; - } else { - return null; - } - }; + 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) { + Tour.prototype._callOnPromiseDone = function(promise, cb, arg) { + if (promise) { + return promise.then((function(_this) { + return function(e) { return cb.call(_this, arg); - }); - } else { - return cb.call(this, arg); - } - }; + }; + })(this)); + } 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._showBackdrop = function(element) { + if (this.backdrop.backgroundShown) { + return; + } + this.backdrop = $("
    ", { + "class": "tour-backdrop" + }); + this.backdrop.backgroundShown = true; + return $("body").append(this.backdrop); + }; - Tour.prototype._hideBackdrop = function() { - if (this.backdrop.overlay === null) { - return; - } - if (this.backdrop.$element) { - this._hideOverlayElement(); - } - return this._hideOverlay(); - }; + Tour.prototype._hideBackdrop = function() { + this._hideOverlayElement(); + return this._hideBackground(); + }; - Tour.prototype._showOverlay = function() { - this.backdrop = $("
    ", { - "class": "tour-backdrop" - }); - return $("body").append(this.backdrop); - }; + Tour.prototype._hideBackground = function() { + this.backdrop.remove(); + this.backdrop.overlay = null; + return this.backdrop.backgroundShown = false; + }; - Tour.prototype._hideOverlay = function() { - this.backdrop.remove(); - return this.backdrop.overlay = null; - }; + Tour.prototype._showOverlayElement = function(element) { + var $background, $element, offset; + $element = $(element); + if (!$element || $element.length === 0 || this.backdrop.overlayElementShown) { + return; + } + this.backdrop.overlayElementShown = true; + $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._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() { + if (!this.backdrop.overlayElementShown) { + return; + } + this.backdrop.$element.removeClass("tour-step-backdrop"); + this.backdrop.$background.remove(); + this.backdrop.$element = null; + this.backdrop.$background = null; + return this.backdrop.overlayElementShown = false; + }; - Tour.prototype._hideOverlayElement = function() { - this.backdrop.$element.removeClass("tour-step-backdrop"); - this.backdrop.$background.remove(); - this.backdrop.$element = null; - return this.backdrop.$background = null; - }; + Tour.prototype._clearTimer = function() { + window.clearTimeout(this._timer); + this._timer = null; + return this._duration = null; + }; - return Tour; + return Tour; - })(); - return window.Tour = Tour; - })(jQuery, window); - -}).call(this); + })(); + return window.Tour = Tour; +})(jQuery, window); diff --git a/addons/website/static/src/js/website.tour.banner.js b/addons/website/static/src/js/website.tour.banner.js index 65099d03986..443405e2401 100644 --- a/addons/website/static/src/js/website.tour.banner.js +++ b/addons/website/static/src/js/website.tour.banner.js @@ -15,7 +15,6 @@ popover: { next: _t("Start Tutorial"), end: _t("Skip It") }, }, { - waitNot: '.popover.tour', element: 'button[data-action=edit]', placement: 'bottom', title: _t("Edit this page"), @@ -52,7 +51,6 @@ popover: { next: _t("Continue") }, }, { - waitNot: '.popover.tour', element: 'button[data-action=snippet]', placement: 'bottom', title: _t("Add Another Block"), @@ -81,7 +79,6 @@ popover: { next: _t("Continue") }, }, { - waitNot: '.popover.tour', element: 'a[data-action=show-mobile-preview]', placement: 'bottom', title: _t("Test Your Mobile Version"), diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index fc0dd513cf2..df6108e0278 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -32,7 +32,7 @@ if (website.EditorBar) { var self = this; var menu = $('#help-menu'); _.each(website.Tour.tours, function (tour) { - if (tour.mode != "tutorial") { + if (tour.mode === "test") { return; } var $menuItem = $($.parseHTML('
  • '+tour.name+'
  • ')); @@ -90,16 +90,19 @@ website.Tour = {}; website.Tour.tours = {}; website.Tour.state = null; website.Tour.register = function (tour) { + if (tour.mode !== "test") tour.mode = "tutorial"; website.Tour.tours[tour.id] = tour; }; website.Tour.run = function (tour_id, mode) { - if (localStorage.getItem("tour")) { // only one test running + if (localStorage.getItem("tour") && mode === "test") { // only one test running return; } var tour = website.Tour.tours[tour_id]; - website.Tour.save_state(tour.id, mode || tour.mode, 0); - if (tour.path) { + website.Tour.saveState(tour.id, mode || tour.mode, 0); + if (tour.path && !window.location.href.match(new RegExp("("+website.Tour.getLang()+")?"+tour.path+"#?$", "i"))) { window.location.href = "/"+website.Tour.getLang()+tour.path; + } else { + website.Tour.running(); } }; website.Tour.registerSteps = function (tour) { @@ -114,48 +117,82 @@ website.Tour.registerSteps = function (tour) { if (!step.waitNot && index > 0 && tour.steps[index-1] && tour.steps[index-1].popover && tour.steps[index-1].popover.next) { - step.waitNot = '.popover.tour:visible'; + step.waitNot = '.popover.tour.fade.in:visible'; } if (!step.waitFor && index > 0 && tour.steps[index-1].snippet) { step.waitFor = '.oe_overlay_options .oe_options:visible'; } - if (!step.element) step.orphan = true; - - var snippet = step.element.match(/#oe_snippets (.*) \.oe_snippet_thumbnail/); + 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.orphan = true; } if (tour.steps[index-1] && tour.steps[index-1].popover && tour.steps[index-1].popover.next) { var step = { - step_id: index, - waitNot: '.popover.tour:visible' + id: index, + waitNot: '.popover.tour.fade.in:visible' }; tour.steps.push(step); } // rendering bootstrap tour and popover - if (tour.mode != "test" || typeof Tour !== "undefined") { + if (tour.mode !== "test" || typeof Tour !== "undefined") { tour.tour = new Tour({ - name: this.id, + debug: true, + name: tour.id, storage: localStorage, keyboard: false, - template: this.popover(), + template: website.Tour.popover(), onHide: function () { window.scrollTo(0, 0); } }); + for (var index=0, len=tour.steps.length; index -1) { - tour_id = window.location.href.match(/#tutorial\.(.*)=true/)[1]; - mode = "tutorial"; - step_id = 0; +website.Tour.getState = function () { + var state = JSON.parse(localStorage.getItem("tour") || 'false') || {}; + 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 + }; } - if (!tour_id) { + if (!state.id) { return; } - var tour = website.Tour.tours[tour_id]; - return {'tour': tour, 'tour_id': tour_id, 'mode': mode, 'step_id': step_id}; + state.tour = website.Tour.tours[state.id]; + state.step = state.tour.steps[state.step_id]; + return state; }; website.Tour.error = function (tour, step, message) { website.Tour.reset(); throw new Error(message + - + "\ntour:" + tour.id + - + "\nstep:" + step.id + ": '" + (step._title || step.title) + "'" + + "\ntour: " + tour.id + + + "\nstep: " + step.id + ": '" + (step._title || step.title) + "'" + '\nhref: ' + window.location.href + '\nreferrer: ' + document.referrer + '\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()) - + "\nlocalStorage: " + localStorage.getItem("tour") + + "\nlocalStorage: " + JSON.stringify(localStorage) + '\n\n' + $("body").html() ); }; @@ -206,24 +242,39 @@ website.Tour.lists = function () { } return tour_ids; }; -website.Tour.save_state = function (tour_id, mode, step_id) { - localStorage.setItem("tour", '{tour_id:'+tour_id+', mode:'+mode+', step_id:'+step_id+'}'); +website.Tour.saveState = function (tour_id, mode, step_id) { + localStorage.setItem("tour", JSON.stringify({"id":tour_id, "mode":mode, "step_id":step_id})); }; website.Tour.reset = function () { - var running = website.Tour.get_state(); - for (var k in running.tour.steps) { - running.tour.steps[k].busy = false; + var state = website.Tour.getState(); + if (state) { + for (var k in state.tour.steps) { + state.tour.steps[k].busy = false; + } + if (state.tour.tour) { + state.tour.tour.end(); + } } localStorage.removeItem("tour"); clearTimeout(website.Tour.timer); clearTimeout(website.Tour.testtimer); - $('.popover.tour').remove(); + $(".popover.tour").remove(); }; website.Tour.running = function () { - var running = website.Tour.get_state(); - website.Tour.registerSteps(running.tour); - website.Tour.nextStep( running.tour, running.step_id ); + var state = website.Tour.getState(); + if (state) { + website.Tour.registerSteps(state.tour); + if ($.ajaxBusy) { + $(document).ajaxStop(function() { + setTimeout(function () { + website.Tour.nextStep( state.tour, state.step, state.mode === "test" ? 5000 : 0 ); + },0); + }); + } else { + website.Tour.nextStep( state.tour, state.step, state.mode === "test" ? 5000 : 0 ); + } + } }; website.Tour.timer = null; @@ -238,83 +289,62 @@ website.Tour.check = function (step) { website.Tour.waitNextStep = function (tour, step, overlaps) { var time = new Date().getTime(); var timer; + var next = tour.steps[step.id+1]; window.onbeforeunload = function () { clearTimeout(website.Tour.timer); clearTimeout(website.Tour.testtimer); }; - // check popover activity - $(".popover.tour button") - .off() - .on("click", function () { - $(".popover.tour").remove(); - if (step.busy) return; - if (!$(this).is("[data-role='next']")) { - clearTimeout(website.Tour.timer); - step.busy = true; - if (tour.tour) { - tour.tour.end(); - } - tour.endTour(tour); - } - }); - function checkNext () { + website.Tour.autoToggleBootstrapTour(); + clearTimeout(website.Tour.timer); - if (step.busy) return; - if (website.Tour.check(step)) { - step.busy = true; + if (next.busy) return; + if (website.Tour.check(next)) { + next.busy = true; + clearTimeout(website.Tour.currentTimer); // use an other timeout for cke dom loading setTimeout(function () { - website.Tour.nextStep(tour, step, overlaps); + website.Tour.nextStep(tour, next, overlaps); }, website.Tour.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); - } - } website.Tour.timer = setTimeout(checkNext, website.Tour.defaultDelay); } else { - website.Tour.error(tour, step, "Can't arrive to the next step"); + website.Tour.error(tour, next, "Can't arrive to the next step"); } } checkNext(); }; +website.Tour.currentTimer = null; website.Tour.nextStep = function (tour, step, overlaps) { - var state = website.Tour.get_state(); - website.Tour.save_state(tour.id, state.mode, step.id); + var state = website.Tour.getState(); + website.Tour.saveState(state.id, state.mode, step.id); - // clear popover (fix for boostrap tour if the element is removed before destroy popover) - $(".popover.tour").remove(); - // go to step in bootstrap tour - if (tour.tour) { - tour.tour.goto(step.id); + website.Tour.autoToggleBootstrapTour(); + + if (step.onload) { + step.onload(); } - step.onload(); var next = tour.steps[step.id+1]; if (next) { setTimeout(function () { - website.Tour.waitNextStep(tour, next, overlaps); + website.Tour.waitNextStep(tour, step, overlaps); if (state.mode === "test") { setTimeout(function(){ website.Tour.autoNextStep(tour, step); }, website.Tour.defaultDelay); } - }, next && next.wait || 0); + }, next.wait || 0); } else { website.Tour.endTour(tour); } }; website.Tour.endTour = function (tour) { - var state = website.Tour.get_state(); - var test = state.step_id >= tour.steps.length-1; - this.reset(); + var state = website.Tour.getState(); + var test = state.step.id >= state.tour.steps.length-1; + website.Tour.reset(); if (test) { console.log('ok'); } else { @@ -388,7 +418,8 @@ website.Tour.autoDragAndDropSnippet = function (selector) { $dropZone.trigger($.Event("mouseup", { which: 1, pageX: dropPosition.left, pageY: dropPosition.top })); }; -website.ready(website.Tour.running); +//$(document).ready(website.Tour.running); +website.ready().then(website.Tour.running); }()); From 58b6aeb01d489922f8311e6a390805d9cb1c877f Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Fri, 21 Mar 2014 12:07:50 +0100 Subject: [PATCH 045/356] [IMP] website tour: remove bootstrap Tour lib bzr revid: chm@openerp.com-20140321110750-ew8rj3nv2jcort1x --- .../lib/bootstrap-tour/bootstrap-tour.css | 65 -- .../lib/bootstrap-tour/bootstrap-tour.js | 716 ------------------ addons/website/static/src/css/editor.css | 24 +- addons/website/static/src/css/editor.sass | 19 +- .../static/src/js/website.tour.banner.js | 2 + addons/website/static/src/js/website.tour.js | 349 +++++---- 6 files changed, 258 insertions(+), 917 deletions(-) delete mode 100644 addons/website/static/lib/bootstrap-tour/bootstrap-tour.css delete mode 100644 addons/website/static/lib/bootstrap-tour/bootstrap-tour.js 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 100644 index 5449158e7f4..00000000000 --- a/addons/website/static/lib/bootstrap-tour/bootstrap-tour.css +++ /dev/null @@ -1,65 +0,0 @@ -/* =========================================================== -# bootstrap-tour - v0.9.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. -*/ -.tour-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1100; - background-color: #000; - opacity: 0.8; -} -.tour-step-backdrop { - position: relative; - z-index: 1101; - background: inherit; -} -.tour-step-background { - position: absolute; - z-index: 1100; - background: inherit; - border-radius: 6px; -} -.popover[class*="tour-"] { - z-index: 1100; -} -.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 100644 index a0172c718c0..00000000000 --- a/addons/website/static/lib/bootstrap-tour/bootstrap-tour.js +++ /dev/null @@ -1,716 +0,0 @@ -/* =========================================================== -# bootstrap-tour - v0.9.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($, window) { - var Tour, document; - document = window.document; - Tour = (function() { - function Tour(options) { - this._options = $.extend({ - name: "tour", - steps: [], - container: "body", - keyboard: true, - storage: window.localStorage, - debug: false, - backdrop: false, - redirect: true, - orphan: false, - duration: false, - basePath: "", - template: "

    ", - afterSetState: function(key, value) {}, - afterGetState: function(key, value) {}, - afterRemoveState: function(key) {}, - onStart: function(tour) {}, - onEnd: function(tour) {}, - onShow: function(tour) {}, - onShown: function(tour) {}, - onHide: function(tour) {}, - onHidden: function(tour) {}, - onNext: function(tour) {}, - onPrev: function(tour) {}, - onPause: function(tour, duration) {}, - onResume: function(tour, duration) {} - }, options); - this._force = false; - this._inited = false; - this.backdrop = { - overlay: null, - $element: null, - $background: null, - backgroundShown: false, - overlayElementShown: false - }; - this; - } - - Tour.prototype.addSteps = function(steps) { - var step, _i, _len; - for (_i = 0, _len = steps.length; _i < _len; _i++) { - step = steps[_i]; - this.addStep(step); - } - return this; - }; - - Tour.prototype.addStep = function(step) { - this._options.steps.push(step); - return this; - }; - - Tour.prototype.getStep = function(i) { - if (this._options.steps[i] != null) { - return $.extend({ - id: "step-" + i, - path: "", - placement: "right", - title: "", - content: "

    ", - next: i === this._options.steps.length - 1 ? -1 : i + 1, - prev: i - 1, - animation: true, - container: this._options.container, - backdrop: this._options.backdrop, - redirect: this._options.redirect, - orphan: this._options.orphan, - duration: this._options.duration, - template: this._options.template, - onShow: this._options.onShow, - onShown: this._options.onShown, - onHide: this._options.onHide, - onHidden: this._options.onHidden, - onNext: this._options.onNext, - onPrev: this._options.onPrev, - onPause: this._options.onPause, - onResume: this._options.onResume - }, this._options.steps[i]); - } - }; - - Tour.prototype.init = function(force) { - this._force = force; - if (this.ended()) { - this._debug("Tour ended, init prevented."); - return this; - } - this.setCurrentStep(); - this._initMouseNavigation(); - this._initKeyboardNavigation(); - this._onResize((function(_this) { - return function() { - return _this.showStep(_this._current); - }; - })(this)); - if (this._current !== null) { - this.showStep(this._current); - } - this._inited = true; - return this; - }; - - Tour.prototype.start = function(force) { - var promise; - if (force == null) { - force = false; - } - if (!this._inited) { - this.init(force); - } - if (this._current === null) { - promise = this._makePromise(this._options.onStart != null ? this._options.onStart(this) : void 0); - this._callOnPromiseDone(promise, this.showStep, 0); - } - return this; - }; - - Tour.prototype.next = function() { - var promise; - promise = this.hideStep(this._current); - return this._callOnPromiseDone(promise, this._showNextStep); - }; - - Tour.prototype.prev = function() { - var promise; - promise = this.hideStep(this._current); - return this._callOnPromiseDone(promise, this._showPrevStep); - }; - - Tour.prototype.goTo = function(i) { - var promise; - promise = this.hideStep(this._current); - return this._callOnPromiseDone(promise, this.showStep, i); - }; - - Tour.prototype.end = function() { - var endHelper, promise; - endHelper = (function(_this) { - return function(e) { - $(document).off("click.tour-" + _this._options.name); - $(document).off("keyup.tour-" + _this._options.name); - $(window).off("resize.tour-" + _this._options.name); - _this._setState("end", "yes"); - _this._inited = false; - _this._force = false; - _this._clearTimer(); - if (_this._options.onEnd != null) { - return _this._options.onEnd(_this); - } - }; - })(this); - promise = this.hideStep(this._current); - return this._callOnPromiseDone(promise, endHelper); - }; - - Tour.prototype.ended = function() { - return !this._force && !!this._getState("end"); - }; - - Tour.prototype.restart = function() { - this._removeState("current_step"); - this._removeState("end"); - this.setCurrentStep(0); - return this.start(); - }; - - Tour.prototype.pause = function() { - var step; - step = this.getStep(this._current); - if (!(step && step.duration)) { - return this; - } - this._paused = true; - this._duration -= new Date().getTime() - this._start; - window.clearTimeout(this._timer); - this._debug("Paused/Stopped step " + (this._current + 1) + " timer (" + this._duration + " remaining)."); - if (step.onPause != null) { - return step.onPause(this, this._duration); - } - }; - - Tour.prototype.resume = function() { - var step; - step = this.getStep(this._current); - if (!(step && step.duration)) { - return this; - } - this._paused = false; - this._start = new Date().getTime(); - this._duration = this._duration || step.duration; - this._timer = window.setTimeout((function(_this) { - return function() { - if (_this._isLast()) { - return _this.next(); - } else { - return _this.end(); - } - }; - })(this), this._duration); - this._debug("Started step " + (this._current + 1) + " timer with duration " + this._duration); - if ((step.onResume != null) && this._duration !== step.duration) { - return step.onResume(this, this._duration); - } - }; - - Tour.prototype.hideStep = function(i) { - var hideStepHelper, promise, step; - step = this.getStep(i); - if (!step) { - return; - } - this._clearTimer(); - promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0); - hideStepHelper = (function(_this) { - return function(e) { - var $element; - $element = $(step.element); - if (!($element.data("bs.popover") || $element.data("popover"))) { - $element = $("body"); - } - $element.popover("destroy").removeClass("tour-" + _this._options.name + "-element tour-" + _this._options.name + "-" + i + "-element"); - if (step.reflex) { - $element.css("cursor", "").off("click.tour-" + _this._options.name); - } - if (step.backdrop) { - _this._hideBackdrop(); - } - if (step.onHidden != null) { - return step.onHidden(_this); - } - }; - })(this); - this._callOnPromiseDone(promise, hideStepHelper); - return promise; - }; - - Tour.prototype.showStep = function(i) { - var promise, showStepHelper, skipToPrevious, step; - if (this.ended()) { - this._debug("Tour ended, showStep prevented."); - return this; - } - step = this.getStep(i); - if (!step) { - return; - } - skipToPrevious = i < this._current; - promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0); - showStepHelper = (function(_this) { - return function(e) { - var current_path, path; - _this.setCurrentStep(i); - path = (function() { - switch ({}.toString.call(step.path)) { - case "[object Function]": - return step.path(); - case "[object String]": - return this._options.basePath + step.path; - default: - return step.path; - } - }).call(_this); - current_path = [document.location.pathname, document.location.hash].join(""); - if (_this._isRedirect(path, current_path)) { - _this._redirect(step, path); - return; - } - if (_this._isOrphan(step)) { - if (!step.orphan) { - _this._debug("Skip the orphan step " + (_this._current + 1) + ". Orphan option is false and the element doesn't exist or is hidden."); - if (skipToPrevious) { - _this._showPrevStep(); - } else { - _this._showNextStep(); - } - return; - } - _this._debug("Show the orphan step " + (_this._current + 1) + ". Orphans option is true."); - } - if (step.backdrop) { - _this._showBackdrop(!_this._isOrphan(step) ? step.element : void 0); - } - _this._scrollIntoView(step.element, function() { - if ((step.element != null) && step.backdrop) { - _this._showOverlayElement(step.element); - } - _this._showPopover(step, i); - if (step.onShown != null) { - step.onShown(_this); - } - return _this._debug("Step " + (_this._current + 1) + " of " + _this._options.steps.length); - }); - if (step.duration) { - return _this.resume(); - } - }; - })(this); - this._callOnPromiseDone(promise, showStepHelper); - return promise; - }; - - Tour.prototype.getCurrentStep = function() { - return this._current; - }; - - Tour.prototype.setCurrentStep = function(value) { - if (value != null) { - this._current = value; - this._setState("current_step", value); - } else { - this._current = this._getState("current_step"); - this._current = this._current === null ? null : parseInt(this._current, 10); - } - return this; - }; - - Tour.prototype._setState = function(key, value) { - var e, keyName; - if (this._options.storage) { - keyName = "" + this._options.name + "_" + key; - try { - this._options.storage.setItem(keyName, value); - } catch (_error) { - e = _error; - if (e.code === DOMException.QUOTA_EXCEEDED_ERR) { - this.debug("LocalStorage quota exceeded. State storage failed."); - } - } - return this._options.afterSetState(keyName, value); - } else { - if (this._state == null) { - this._state = {}; - } - return this._state[key] = value; - } - }; - - Tour.prototype._removeState = function(key) { - var keyName; - if (this._options.storage) { - keyName = "" + this._options.name + "_" + key; - this._options.storage.removeItem(keyName); - return this._options.afterRemoveState(keyName); - } else { - if (this._state != null) { - return delete this._state[key]; - } - } - }; - - Tour.prototype._getState = function(key) { - var keyName, value; - if (this._options.storage) { - keyName = "" + this._options.name + "_" + key; - value = this._options.storage.getItem(keyName); - } else { - if (this._state != null) { - value = this._state[key]; - } - } - if (value === void 0 || value === "null") { - value = null; - } - this._options.afterGetState(key, value); - return value; - }; - - Tour.prototype._showNextStep = function() { - var promise, showNextStepHelper, step; - step = this.getStep(this._current); - showNextStepHelper = (function(_this) { - return function(e) { - return _this.showStep(step.next); - }; - })(this); - promise = this._makePromise(step.onNext != null ? step.onNext(this) : void 0); - return this._callOnPromiseDone(promise, showNextStepHelper); - }; - - Tour.prototype._showPrevStep = function() { - var promise, showPrevStepHelper, step; - step = this.getStep(this._current); - showPrevStepHelper = (function(_this) { - return function(e) { - return _this.showStep(step.prev); - }; - })(this); - promise = this._makePromise(step.onPrev != null ? step.onPrev(this) : void 0); - return this._callOnPromiseDone(promise, showPrevStepHelper); - }; - - Tour.prototype._debug = function(text) { - if (this._options.debug) { - return window.console.log("Bootstrap Tour '" + this._options.name + "' | " + text); - } - }; - - Tour.prototype._isRedirect = function(path, currentPath) { - return (path != null) && path !== "" && ((toString.call(path) === "[object RegExp]" && !path.test(currentPath)) || (toString.call(path) === "[object String]" && path.replace(/\?.*$/, "").replace(/\/?$/, "") !== currentPath.replace(/\/?$/, ""))); - }; - - Tour.prototype._redirect = function(step, path) { - if ($.isFunction(step.redirect)) { - return step.redirect.call(this, path); - } else if (step.redirect === true) { - this._debug("Redirect to " + path); - return document.location.href = path; - } - }; - - Tour.prototype._isOrphan = function(step) { - return (step.element == null) || !$(step.element).length || $(step.element).is(":hidden") && ($(step.element)[0].namespaceURI !== "http://www.w3.org/2000/svg"); - }; - - Tour.prototype._isLast = function() { - return this._current < this._options.steps.length - 1; - }; - - Tour.prototype._showPopover = function(step, i) { - var $element, $navigation, $template, $tip, isOrphan, options; - options = $.extend({}, this._options); - $template = $.isFunction(step.template) ? $(step.template(i, step)) : $(step.template); - $navigation = $template.find(".popover-navigation"); - isOrphan = this._isOrphan(step); - if (isOrphan) { - step.element = "body>*:last"; - step.placement = "top"; - $template = $template.addClass("orphan"); - } - $element = $(step.element); - $template.addClass("tour-" + this._options.name + " tour-" + this._options.name + "-" + i); - $element.addClass("tour-" + this._options.name + "-element tour-" + this._options.name + "-" + i + "-element"); - if (step.options) { - $.extend(options, step.options); - } - if (step.reflex) { - $element.css("cursor", "pointer").on("click.tour-" + this._options.name, (function(_this) { - return function() { - if (_this._isLast()) { - return _this.next(); - } else { - return _this.end(); - } - }; - })(this)); - } - if (step.prev < 0) { - $navigation.find("[data-role='prev']").addClass("disabled"); - } - if (step.next < 0) { - $navigation.find("[data-role='next']").addClass("disabled"); - } - if (!step.duration) { - $navigation.find("[data-role='pause-resume']").remove(); - } - step.template = $template.clone().wrap("
    ").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._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(element, callback) { - var $element, $window, counter, offsetTop, scrollTop, windowHeight; - $element = $(element); - if (!$element.length) { - return callback(); - } - $window = $(window); - offsetTop = $element.offset().top; - windowHeight = $window.height(); - scrollTop = Math.max(0, offsetTop - (windowHeight / 2)); - this._debug("Scroll into view. ScrollTop: " + scrollTop + ". Element offset: " + offsetTop + ". Window height: " + windowHeight + "."); - counter = 0; - return $("body,html").stop(true, true).animate({ - scrollTop: Math.ceil(scrollTop) - }, (function(_this) { - return function() { - if (++counter === 2) { - callback(); - return _this._debug("Scroll into view. Animation end element offset: " + ($element.offset().top) + ". Window height: " + ($window.height()) + "."); - } - }; - })(this)); - }; - - Tour.prototype._onResize = function(callback, timeout) { - return $(window).on("resize.tour-" + this._options.name, function() { - clearTimeout(timeout); - return timeout = setTimeout(callback, 100); - }); - }; - - Tour.prototype._initMouseNavigation = function() { - var _this; - _this = this; - return $(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']:not(.disabled)").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']:not(.disabled)").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']").off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='next']:not(.disabled)", (function(_this) { - return function(e) { - e.preventDefault(); - return _this.next(); - }; - })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='prev']:not(.disabled)", (function(_this) { - return function(e) { - e.preventDefault(); - return _this.prev(); - }; - })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='end']", (function(_this) { - return function(e) { - e.preventDefault(); - return _this.end(); - }; - })(this)).on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role='pause-resume']", function(e) { - var $this; - e.preventDefault(); - $this = $(this); - $this.text(_this._paused ? $this.data("pause-text") : $this.data("resume-text")); - if (_this._paused) { - return _this.resume(); - } else { - return _this.pause(); - } - }); - }; - - Tour.prototype._initKeyboardNavigation = function() { - if (!this._options.keyboard) { - return; - } - return $(document).on("keyup.tour-" + this._options.name, (function(_this) { - return function(e) { - if (!e.which) { - return; - } - switch (e.which) { - case 39: - e.preventDefault(); - if (_this._isLast()) { - 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(); - } - }; - })(this)); - }; - - Tour.prototype._makePromise = function(result) { - if (result && $.isFunction(result.then)) { - return result; - } else { - return null; - } - }; - - Tour.prototype._callOnPromiseDone = function(promise, cb, arg) { - if (promise) { - return promise.then((function(_this) { - return function(e) { - return cb.call(_this, arg); - }; - })(this)); - } else { - return cb.call(this, arg); - } - }; - - Tour.prototype._showBackdrop = function(element) { - if (this.backdrop.backgroundShown) { - return; - } - this.backdrop = $("
    ", { - "class": "tour-backdrop" - }); - this.backdrop.backgroundShown = true; - return $("body").append(this.backdrop); - }; - - Tour.prototype._hideBackdrop = function() { - this._hideOverlayElement(); - return this._hideBackground(); - }; - - Tour.prototype._hideBackground = function() { - this.backdrop.remove(); - this.backdrop.overlay = null; - return this.backdrop.backgroundShown = false; - }; - - Tour.prototype._showOverlayElement = function(element) { - var $background, $element, offset; - $element = $(element); - if (!$element || $element.length === 0 || this.backdrop.overlayElementShown) { - return; - } - this.backdrop.overlayElementShown = true; - $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() { - if (!this.backdrop.overlayElementShown) { - return; - } - this.backdrop.$element.removeClass("tour-step-backdrop"); - this.backdrop.$background.remove(); - this.backdrop.$element = null; - this.backdrop.$background = null; - return this.backdrop.overlayElementShown = false; - }; - - Tour.prototype._clearTimer = function() { - window.clearTimeout(this._timer); - this._timer = null; - return this._duration = null; - }; - - return Tour; - - })(); - return window.Tour = Tour; -})(jQuery, window); diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index da0fad43caf..0bd20bdcca9 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -484,10 +484,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 4571dcd257e..bd0f4628009 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -424,9 +424,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.tour.banner.js b/addons/website/static/src/js/website.tour.banner.js index 443405e2401..a8abbcf59e2 100644 --- a/addons/website/static/src/js/website.tour.banner.js +++ b/addons/website/static/src/js/website.tour.banner.js @@ -13,6 +13,7 @@ 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") }, + backdrop: true, }, { element: 'button[data-action=edit]', @@ -77,6 +78,7 @@ title: _t("Good Job!"), content: _t("Well done, you created your homepage."), popover: { next: _t("Continue") }, + backdrop: true, }, { element: 'a[data-action=show-mobile-preview]', diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index df6108e0278..3bfd4e06da0 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -15,8 +15,8 @@ if (typeof openerp === "undefined") { var website = window.openerp.website; -// don't rewrite website.Tour in test mode -if (typeof website.Tour !== "undefined") { +// don't rewrite T in test mode +if (typeof T !== "undefined") { return; } @@ -31,14 +31,14 @@ if (website.EditorBar) { start: function () { var self = this; var menu = $('#help-menu'); - _.each(website.Tour.tours, function (tour) { + _.each(T.tours, function (tour) { if (tour.mode === "test") { return; } var $menuItem = $($.parseHTML('
  • '+tour.name+'
  • ')); $menuItem.click(function () { - website.Tour.reset(); - website.Tour.run(tour.id); + T.reset(); + T.run(tour.id); }); menu.append($menuItem); }); @@ -80,32 +80,36 @@ $.ajaxSetup({ } }); - ///////////////////////////////////////////////// - var localStorage = window.localStorage; -website.Tour = {}; -website.Tour.tours = {}; -website.Tour.state = null; -website.Tour.register = function (tour) { +var T = website.Tour = {}; +T.tours = {}; +T.defaultDelay = 50; +T.errorDelay = 5000; +T.state = null; +T.$element = null; +T.timer = null; +T.testtimer = null; +T.currentTimer = null; +T.register = function (tour) { if (tour.mode !== "test") tour.mode = "tutorial"; - website.Tour.tours[tour.id] = tour; + T.tours[tour.id] = tour; }; -website.Tour.run = function (tour_id, mode) { +T.run = function (tour_id, mode) { if (localStorage.getItem("tour") && mode === "test") { // only one test running return; } - var tour = website.Tour.tours[tour_id]; - website.Tour.saveState(tour.id, mode || tour.mode, 0); - if (tour.path && !window.location.href.match(new RegExp("("+website.Tour.getLang()+")?"+tour.path+"#?$", "i"))) { - window.location.href = "/"+website.Tour.getLang()+tour.path; + var tour = T.tours[tour_id]; + T.saveState(tour.id, mode || tour.mode, 0); + if (tour.path && !window.location.href.match(new RegExp("("+T.getLang()+")?"+tour.path+"#?$", "i"))) { + window.location.href = "/"+T.getLang()+tour.path; } else { - website.Tour.running(); + T.running(); } }; -website.Tour.registerSteps = function (tour) { +T.registerSteps = function (tour) { if (tour.register) { return; } @@ -130,7 +134,10 @@ website.Tour.registerSteps = function (tour) { step.element = '#oe_snippets '+step.snippet+' .oe_snippet_thumbnail'; } - if (!step.element) step.orphan = true; + if (!step.element) { + step.element = "body"; + step.orphan = true; + } } if (tour.steps[index-1] && tour.steps[index-1].popover && tour.steps[index-1].popover.next) { @@ -142,69 +149,151 @@ website.Tour.registerSteps = function (tour) { } // rendering bootstrap tour and popover - if (tour.mode !== "test" || typeof Tour !== "undefined") { - tour.tour = new Tour({ - debug: true, - name: tour.id, - storage: localStorage, - keyboard: false, - template: website.Tour.popover(), - onHide: function () { - window.scrollTo(0, 0); - } - }); - + 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(); +}; +T.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 { - tour.end(); + var top = T.$element.offset().top + T.$element.outerHeight()/2 - tipOffset.top; + $tip.find(".arrow").css("top", top ? top + "px" : ""); } }; -website.Tour.popoverTitle = function (tour, options) { +T.popoverTitle = function (tour, options) { return openerp.qweb.render('website.tour_popover_title', options); }; -website.Tour.popover = function (options) { +T.popover = function (options) { return openerp.qweb.render('website.tour_popover', options); }; -website.Tour.getLang = function () { +T.getLang = function () { return $("html").attr("lang").replace(/-/, '_'); }; -website.Tour.getState = function () { +T.getState = function () { var state = JSON.parse(localStorage.getItem("tour") || 'false') || {}; var tour_id,mode,step_id; if (!state.id && window.location.href.indexOf("#tutorial.") > -1) { @@ -217,15 +306,16 @@ website.Tour.getState = function () { if (!state.id) { return; } - state.tour = website.Tour.tours[state.id]; + state.tour = T.tours[state.id]; state.step = state.tour.steps[state.step_id]; return state; }; -website.Tour.error = function (tour, step, message) { - website.Tour.reset(); +T.error = function (message) { + var state = T.getState(); + T.reset(); throw new Error(message + - + "\ntour: " + tour.id + - + "\nstep: " + step.id + ": '" + (step._title || step.title) + "'" + + "\ntour: " + state.tour.id + + + "\nstep: " + state.step.id + ": '" + (state.step._title || state.step.title) + "'" + '\nhref: ' + window.location.href + '\nreferrer: ' + document.referrer + '\nelement: ' + Boolean(!step.element || ($(step.element).size() && $(step.element).is(":visible") && !$(step.element).is(":hidden"))) @@ -235,124 +325,121 @@ website.Tour.error = function (tour, step, message) { + '\n\n' + $("body").html() ); }; -website.Tour.lists = function () { +T.lists = function () { var tour_ids = []; - for (var k in website.Tour.tours) { + for (var k in T.tours) { tour_ids.push(k); } return tour_ids; }; -website.Tour.saveState = function (tour_id, mode, step_id) { - localStorage.setItem("tour", JSON.stringify({"id":tour_id, "mode":mode, "step_id":step_id})); +T.saveState = function (tour_id, mode, step_id) { + localStorage.setItem("tour", JSON.stringify({"id":tour_id, "mode":mode, "step_id":step_id || 0})); }; -website.Tour.reset = function () { - var state = website.Tour.getState(); +T.reset = function () { + var state = T.getState(); if (state) { for (var k in state.tour.steps) { state.tour.steps[k].busy = false; } - if (state.tour.tour) { - state.tour.tour.end(); - } } localStorage.removeItem("tour"); - clearTimeout(website.Tour.timer); - clearTimeout(website.Tour.testtimer); - - $(".popover.tour").remove(); + clearTimeout(T.timer); + clearTimeout(T.testtimer); + T.closePopover(); }; -website.Tour.running = function () { - var state = website.Tour.getState(); +T.running = function () { + var state = T.getState(); if (state) { - website.Tour.registerSteps(state.tour); + T.registerSteps(state.tour); if ($.ajaxBusy) { $(document).ajaxStop(function() { setTimeout(function () { - website.Tour.nextStep( state.tour, state.step, state.mode === "test" ? 5000 : 0 ); + T.nextStep(); },0); }); } else { - website.Tour.nextStep( state.tour, state.step, state.mode === "test" ? 5000 : 0 ); + T.nextStep(); } } }; - -website.Tour.timer = null; -website.Tour.testtimer = null; -website.Tour.defaultDelay = 50; -website.Tour.check = function (step) { +T.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())); }; -website.Tour.waitNextStep = function (tour, step, overlaps) { +T.waitNextStep = function () { + var state = T.getState(); var time = new Date().getTime(); var timer; - var next = tour.steps[step.id+1]; + var next = state.tour.steps[state.step.id+1]; + var overlaps = state.mode === "test" ? T.errorDelay : 0; window.onbeforeunload = function () { - clearTimeout(website.Tour.timer); - clearTimeout(website.Tour.testtimer); + clearTimeout(T.timer); + clearTimeout(T.testtimer); }; function checkNext () { - website.Tour.autoToggleBootstrapTour(); + T.autoTogglePopover(); - clearTimeout(website.Tour.timer); - if (next.busy) return; - if (website.Tour.check(next)) { - next.busy = true; - clearTimeout(website.Tour.currentTimer); + clearTimeout(T.timer); + if (T.check(next)) { + clearTimeout(T.currentTimer); // use an other timeout for cke dom loading setTimeout(function () { - website.Tour.nextStep(tour, next, overlaps); - }, website.Tour.defaultDelay); + T.nextStep(next); + }, T.defaultDelay); } else if (!overlaps || new Date().getTime() - time < overlaps) { - website.Tour.timer = setTimeout(checkNext, website.Tour.defaultDelay); + T.timer = setTimeout(checkNext, T.defaultDelay); } else { - website.Tour.error(tour, next, "Can't arrive to the next step"); + T.error("Can't arrive to the next step"); } } checkNext(); }; -website.Tour.currentTimer = null; -website.Tour.nextStep = function (tour, step, overlaps) { - var state = website.Tour.getState(); - website.Tour.saveState(state.id, state.mode, step.id); +T.nextStep = function (step) { + var state = T.getState(); - website.Tour.autoToggleBootstrapTour(); + if (!state) { + return; + } + + step = step || state.step; + T.saveState(state.id, state.mode, step.id); + + T.autoTogglePopover(true); if (step.onload) { step.onload(); } - var next = tour.steps[step.id+1]; + var next = state.tour.steps[step.id+1]; if (next) { setTimeout(function () { - website.Tour.waitNextStep(tour, step, overlaps); + T.waitNextStep(); if (state.mode === "test") { setTimeout(function(){ - website.Tour.autoNextStep(tour, step); - }, website.Tour.defaultDelay); + T.autoNextStep(); + }, T.defaultDelay); } }, next.wait || 0); } else { - website.Tour.endTour(tour); + T.endTour(); } }; -website.Tour.endTour = function (tour) { - var state = website.Tour.getState(); +T.endTour = function () { + var state = T.getState(); var test = state.step.id >= state.tour.steps.length-1; - website.Tour.reset(); + T.reset(); if (test) { console.log('ok'); } else { console.log('error'); } }; -website.Tour.autoNextStep = function (tour, step) { - clearTimeout(website.Tour.testtimer); +T.autoNextStep = function (tour, step) { + clearTimeout(T.testtimer); function autoStep () { if (!step) return; @@ -361,18 +448,14 @@ website.Tour.autoNextStep = function (tour, step) { 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(); - } + T.closePopover(); var $element = $(step.element); if (!$element.size()) return; if (step.snippet) { - website.Tour.autoDragAndDropSnippet($element); + T.autoDragAndDropSnippet($element); } else if (step.sampleText) { @@ -388,7 +471,7 @@ website.Tour.autoNextStep = function (tour, step) { setTimeout(function () { $element.trigger($.Event("keyup", { srcElement: $element })); $element.trigger($.Event("change", { srcElement: $element })); - }, website.Tour.defaultDelay<<1); + }, T.defaultDelay<<1); } else if ($element.is(":visible")) { @@ -406,9 +489,9 @@ website.Tour.autoNextStep = function (tour, step) { }, 1000); } } - website.Tour.testtimer = setTimeout(autoStep, 100); + T.testtimer = setTimeout(autoStep, 100); }; -website.Tour.autoDragAndDropSnippet = function (selector) { +T.autoDragAndDropSnippet = function (selector) { var $thumbnail = $(selector).first(); var thumbnailPosition = $thumbnail.position(); $thumbnail.trigger($.Event("mousedown", { which: 1, pageX: thumbnailPosition.left, pageY: thumbnailPosition.top })); @@ -418,8 +501,8 @@ website.Tour.autoDragAndDropSnippet = function (selector) { $dropZone.trigger($.Event("mouseup", { which: 1, pageX: dropPosition.left, pageY: dropPosition.top })); }; -//$(document).ready(website.Tour.running); -website.ready().then(website.Tour.running); +//$(document).ready(T.running); +website.ready().then(T.running); }()); From 254b3a29fa80ffcaeec2da60a2a2b0abac8da925 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Fri, 21 Mar 2014 12:18:15 +0100 Subject: [PATCH 046/356] [IMP] website tour: auto set backdrop if no selector element bzr revid: chm@openerp.com-20140321111815-t0312xxl7l83w9rx --- addons/website/static/src/js/website.tour.banner.js | 2 -- addons/website/static/src/js/website.tour.js | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/website/static/src/js/website.tour.banner.js b/addons/website/static/src/js/website.tour.banner.js index a8abbcf59e2..443405e2401 100644 --- a/addons/website/static/src/js/website.tour.banner.js +++ b/addons/website/static/src/js/website.tour.banner.js @@ -13,7 +13,6 @@ 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") }, - backdrop: true, }, { element: 'button[data-action=edit]', @@ -78,7 +77,6 @@ title: _t("Good Job!"), content: _t("Well done, you created your homepage."), popover: { next: _t("Continue") }, - backdrop: true, }, { element: 'a[data-action=show-mobile-preview]', diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index 3bfd4e06da0..ce20bf69718 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -137,6 +137,7 @@ T.registerSteps = function (tour) { if (!step.element) { step.element = "body"; step.orphan = true; + step.backdrop = true; } } if (tour.steps[index-1] && From 429324999c9fe7149e34ec5f501c14aa7a8a7fdc Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Fri, 21 Mar 2014 12:23:44 +0100 Subject: [PATCH 047/356] [IMP] website tour: remove tag script and css to lib bootstrap bzr revid: chm@openerp.com-20140321112344-mwh2p8pp02j9sdq0 --- addons/website/views/website_templates.xml | 2 -- addons/website_report/views/layouts.xml | 2 -- 2 files changed, 4 deletions(-) diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 66a38f09651..7849c701bd4 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -253,7 +253,6 @@ - @@ -265,7 +264,6 @@ - diff --git a/addons/website_report/views/layouts.xml b/addons/website_report/views/layouts.xml index 091bb8dd3db..219780317c3 100644 --- a/addons/website_report/views/layouts.xml +++ b/addons/website_report/views/layouts.xml @@ -33,7 +33,6 @@ - @@ -42,7 +41,6 @@ - From d8f27686e5e70a626d12d4fcfdc6af5e5e642468 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Fri, 21 Mar 2014 12:57:24 +0100 Subject: [PATCH 048/356] [IMP] website tour: fix test tour bzr revid: chm@openerp.com-20140321115724-iwu17fkzdzaksmrs --- addons/website/static/src/js/website.tour.js | 25 ++++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index ce20bf69718..26fa5af0b9f 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -313,18 +313,17 @@ T.getState = function () { }; T.error = function (message) { var state = T.getState(); + message += '\n tour: ' + state.id + + '\n step: ' + state.step_id + ": '" + (state.step._title || state.step.title) + "'" + + '\n href: ' + window.location.href + + '\n referrer: ' + document.referrer + + '\n element: ' + Boolean(!state.step.element || ($(state.step.element).size() && $(state.step.element).is(":visible") && !$(state.step.element).is(":hidden"))) + + '\n waitNot: ' + Boolean(!state.step.waitNot || !$(state.step.waitNot).size()) + + '\n waitFor: ' + Boolean(!state.step.waitFor || $(state.step.waitFor).size()) + + "\n localStorage: " + JSON.stringify(localStorage) + + '\n\n' + $("body").html(); T.reset(); - throw new Error(message + - + "\ntour: " + state.tour.id + - + "\nstep: " + state.step.id + ": '" + (state.step._title || state.step.title) + "'" - + '\nhref: ' + window.location.href - + '\nreferrer: ' + document.referrer - + '\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()) - + "\nlocalStorage: " + JSON.stringify(localStorage) - + '\n\n' + $("body").html() - ); + throw new Error(message); }; T.lists = function () { var tour_ids = []; @@ -421,7 +420,7 @@ T.nextStep = function (step) { T.waitNextStep(); if (state.mode === "test") { setTimeout(function(){ - T.autoNextStep(); + T.autoNextStep(state.tour, step); }, T.defaultDelay); } }, next.wait || 0); @@ -449,7 +448,7 @@ T.autoNextStep = function (tour, step) { step.autoComplete(tour); } - T.closePopover(); + $(".popover.tour [data-role='next']").click(); var $element = $(step.element); if (!$element.size()) return; From 89a6bd06495c4fe599756866828298331f375747 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Fri, 21 Mar 2014 13:37:22 +0100 Subject: [PATCH 049/356] [IMP] website tour: fix auto test bzr revid: chm@openerp.com-20140321123722-dmfdckh59cvot00i --- addons/website/static/src/js/website.tour.js | 4 ++-- .../static/src/js/website.tour.event_sale.js | 4 ++-- addons/website_sale/static/src/js/website.tour.sale.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index 26fa5af0b9f..a033e6f1ecd 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -16,7 +16,7 @@ if (typeof openerp === "undefined") { var website = window.openerp.website; // don't rewrite T in test mode -if (typeof T !== "undefined") { +if (typeof website.Tour !== "undefined") { return; } @@ -393,7 +393,7 @@ T.waitNextStep = function () { } else if (!overlaps || new Date().getTime() - time < overlaps) { T.timer = setTimeout(checkNext, T.defaultDelay); } else { - T.error("Can't arrive to the next step"); + T.error("Can't reach the next step"); } } checkNext(); diff --git a/addons/website_event_sale/static/src/js/website.tour.event_sale.js b/addons/website_event_sale/static/src/js/website.tour.event_sale.js index 92e57638cc8..668a05001af 100644 --- a/addons/website_event_sale/static/src/js/website.tour.event_sale.js +++ b/addons/website_event_sale/static/src/js/website.tour.event_sale.js @@ -16,7 +16,7 @@ { title: "go to register page", waitNot: 'a[href*="/event"]:contains("Functional Webinar")', - onload: function () { + autoComplete: function () { // use onload if website_event_track is installed if (!$('form:contains("Ticket Type")').size()) { window.location.href = $('a[href*="/event"][href*="/register"]').attr("href"); @@ -43,7 +43,7 @@ title: "Complete checkout", waitFor: '#top_menu .my_cart_quantity:contains(5)', element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")', - onload: function (tour) { + autoComplete: function (tour) { if ($("input[name='name']").val() === "") $("input[name='name']").val("website_sale-test-shoptest"); if ($("input[name='email']").val() === "") diff --git a/addons/website_sale/static/src/js/website.tour.sale.js b/addons/website_sale/static/src/js/website.tour.sale.js index c7526b2468f..62455e0b811 100644 --- a/addons/website_sale/static/src/js/website.tour.sale.js +++ b/addons/website_sale/static/src/js/website.tour.sale.js @@ -50,7 +50,7 @@ { title: "test with input error", element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")', - onload: function (tour) { + autoComplete: function (tour) { $("input[name='phone']").val(""); }, }, @@ -58,7 +58,7 @@ title: "test without input error", waitFor: 'form[action="/shop/confirm_order"] .has-error', element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")', - onload: function (tour) { + autoComplete: function (tour) { if ($("input[name='name']").val() === "") $("input[name='name']").val("website_sale-test-shoptest"); if ($("input[name='email']").val() === "") From f9b0c7469a7762bbba6d142e7799fc854aa01d24 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Fri, 21 Mar 2014 14:42:07 +0100 Subject: [PATCH 050/356] [IMP] website tour: change syntax bzr revid: chm@openerp.com-20140321134207-y54uj2fynl5p0e8a --- addons/website/static/src/js/website.tour.js | 795 ++++++++++--------- 1 file changed, 399 insertions(+), 396 deletions(-) diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index a033e6f1ecd..6a5f8faab76 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -84,421 +84,424 @@ $.ajaxSetup({ var localStorage = window.localStorage; -var T = website.Tour = {}; -T.tours = {}; -T.defaultDelay = 50; -T.errorDelay = 5000; -T.state = null; -T.$element = null; -T.timer = null; -T.testtimer = null; -T.currentTimer = null; -T.register = function (tour) { - if (tour.mode !== "test") tour.mode = "tutorial"; - T.tours[tour.id] = tour; -}; -T.run = function (tour_id, mode) { - if (localStorage.getItem("tour") && mode === "test") { // only one test running - return; - } - var tour = T.tours[tour_id]; - T.saveState(tour.id, mode || tour.mode, 0); - if (tour.path && !window.location.href.match(new RegExp("("+T.getLang()+")?"+tour.path+"#?$", "i"))) { - window.location.href = "/"+T.getLang()+tour.path; - } else { - T.running(); - } -}; -T.registerSteps = function (tour) { - if (tour.register) { - return; - } - tour.register = true; - - 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'; +var T = website.Tour = { + tours: {}, + defaultDelay: 50, + 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) { + if (localStorage.getItem("tour") && mode === "test") { // only one test running + return; } - if (!step.waitFor && index > 0 && tour.steps[index-1].snippet) { - step.waitFor = '.oe_overlay_options .oe_options:visible'; + var tour = T.tours[tour_id]; + T.saveState(tour.id, mode || tour.mode, 0); + if (tour.path && !window.location.href.match(new RegExp("("+T.getLang()+")?"+tour.path+"#?$", "i"))) { + window.location.href = "/"+T.getLang()+tour.path; + } else { + T.running(); } - - 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'; + }, + registerSteps: function (tour) { + if (tour.register) { + return; } + tour.register = true; - if (!step.element) { - step.element = "body"; - step.orphan = true; - step.backdrop = true; - } - } - if (tour.steps[index-1] && - tour.steps[index-1].popover && tour.steps[index-1].popover.next) { - var step = { - id: index, - waitNot: '.popover.tour.fade.in:visible' - }; - tour.steps.push(step); - } - - // 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(); + if (!step.waitNot && index > 0 && tour.steps[index-1] && + tour.steps[index-1].popover && tour.steps[index-1].popover.next) { + step.waitNot = '.popover.tour.fade.in:visible'; + } + if (!step.waitFor && index > 0 && tour.steps[index-1].snippet) { + step.waitFor = '.oe_overlay_options .oe_options:visible'; } - T.closePopover(); - }); - T.repositionPopover(); -}; -T.repositionPopover = function() { - var popover = T.$element.data("bs.popover"); - var $tip = T.$element.data("bs.popover").tip(); + 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 (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 { - var top = T.$element.offset().top + T.$element.outerHeight()/2 - tipOffset.top; - $tip.find(".arrow").css("top", top ? top + "px" : ""); - } -}; -T.popoverTitle = function (tour, options) { - return openerp.qweb.render('website.tour_popover_title', options); -}; -T.popover = function (options) { - return openerp.qweb.render('website.tour_popover', options); -}; -T.getLang = function () { - return $("html").attr("lang").replace(/-/, '_'); -}; -T.getState = function () { - var state = JSON.parse(localStorage.getItem("tour") || 'false') || {}; - 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 - }; - } - if (!state.id) { - return; - } - state.tour = T.tours[state.id]; - state.step = state.tour.steps[state.step_id]; - return state; -}; -T.error = function (message) { - var state = T.getState(); - message += '\n tour: ' + state.id - + '\n step: ' + state.step_id + ": '" + (state.step._title || state.step.title) + "'" - + '\n href: ' + window.location.href - + '\n referrer: ' + document.referrer - + '\n element: ' + Boolean(!state.step.element || ($(state.step.element).size() && $(state.step.element).is(":visible") && !$(state.step.element).is(":hidden"))) - + '\n waitNot: ' + Boolean(!state.step.waitNot || !$(state.step.waitNot).size()) - + '\n waitFor: ' + Boolean(!state.step.waitFor || $(state.step.waitFor).size()) - + "\n localStorage: " + JSON.stringify(localStorage) - + '\n\n' + $("body").html(); - T.reset(); - throw new Error(message); -}; -T.lists = function () { - var tour_ids = []; - for (var k in T.tours) { - tour_ids.push(k); - } - return tour_ids; -}; -T.saveState = function (tour_id, mode, step_id) { - localStorage.setItem("tour", JSON.stringify({"id":tour_id, "mode":mode, "step_id":step_id || 0})); -}; -T.reset = function () { - var state = T.getState(); - if (state) { - for (var k in state.tour.steps) { - state.tour.steps[k].busy = false; + if (!step.element) { + step.element = "body"; + step.orphan = true; + step.backdrop = true; + } } - } - localStorage.removeItem("tour"); - clearTimeout(T.timer); - clearTimeout(T.testtimer); - T.closePopover(); -}; -T.running = function () { - var state = T.getState(); - if (state) { - T.registerSteps(state.tour); - if ($.ajaxBusy) { - $(document).ajaxStop(function() { - setTimeout(function () { - T.nextStep(); - },0); + if (tour.steps[index-1] && + tour.steps[index-1].popover && tour.steps[index-1].popover.next) { + var step = { + id: index, + waitNot: '.popover.tour.fade.in:visible' + }; + tour.steps.push(step); + } + + // 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(); }); - } else { - T.nextStep(); - } - } -}; -T.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())); -}; -T.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 () { + 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.render('website.tour_popover_title', options); + }, + popover: function (options) { + return openerp.qweb.render('website.tour_popover', options); + }, + getLang: function () { + return $("html").attr("lang").replace(/-/, '_'); + }, + getState: function () { + var state = JSON.parse(localStorage.getItem("tour") || 'false') || {}; + 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 = ""; + T.saveState(state.id, state.mode, state.step_id); + } + if (!state.id) { + return; + } + state.tour = T.tours[state.id]; + state.step = state.tour.steps[state.step_id]; + return state; + }, + error: function (message) { + var state = T.getState(); + message += '\n tour: ' + state.id + + '\n step: ' + state.step_id + ": '" + (state.step._title || state.step.title) + "'" + + '\n href: ' + window.location.href + + '\n referrer: ' + document.referrer + + '\n element: ' + Boolean(!state.step.element || ($(state.step.element).size() && $(state.step.element).is(":visible") && !$(state.step.element).is(":hidden"))) + + '\n waitNot: ' + Boolean(!state.step.waitNot || !$(state.step.waitNot).size()) + + '\n waitFor: ' + Boolean(!state.step.waitFor || $(state.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})); + }, + 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); - }; - - function checkNext () { - T.autoTogglePopover(); - - clearTimeout(T.timer); - if (T.check(next)) { - clearTimeout(T.currentTimer); - // use an other timeout for cke dom loading - setTimeout(function () { - T.nextStep(next); - }, T.defaultDelay); - } else if (!overlaps || new Date().getTime() - time < overlaps) { - T.timer = setTimeout(checkNext, T.defaultDelay); - } else { - T.error("Can't reach the next step"); - } - } - checkNext(); -}; -T.nextStep = function (step) { - var state = T.getState(); - - if (!state) { - return; - } - - step = step || state.step; - T.saveState(state.id, state.mode, step.id); - - T.autoTogglePopover(true); - - if (step.onload) { - step.onload(); - } - - var next = state.tour.steps[step.id+1]; - if (next) { - setTimeout(function () { - T.waitNextStep(); - if (state.mode === "test") { - setTimeout(function(){ - T.autoNextStep(state.tour, step); - }, T.defaultDelay); - } - }, next.wait || 0); - } else { - T.endTour(); - } -}; -T.endTour = function () { - 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'); - } -}; -T.autoNextStep = function (tour, step) { - clearTimeout(T.testtimer); - - function autoStep () { - if (!step) return; - - if (step.autoComplete) { - step.autoComplete(tour); - } - - $(".popover.tour [data-role='next']").click(); - - var $element = $(step.element); - if (!$element.size()) return; - - if (step.snippet) { - - T.autoDragAndDropSnippet($element); - - } else if (step.sampleText) { - - $element.trigger($.Event("keydown", { srcElement: $element })); - if ($element.is("input") ) { - $element.val(step.sampleText); - } if ($element.is("select")) { - $element.find("[value='"+step.sampleText+"'], option:contains('"+step.sampleText+"')").attr("selected", true); - $element.val(step.sampleText); + T.closePopover(); + }, + running: function () { + var state = T.getState(); + if (state) { + T.registerSteps(state.tour); + if ($.ajaxBusy) { + $(document).ajaxStop(function() { + setTimeout(function () { + T.nextStep(); + },0); + }); } else { - $element.html(step.sampleText); + T.nextStep(); } - setTimeout(function () { - $element.trigger($.Event("keyup", { srcElement: $element })); - $element.trigger($.Event("change", { srcElement: $element })); - }, T.defaultDelay<<1); - - } else if ($element.is(":visible")) { - - $element.trigger($.Event("mouseenter", { srcElement: $element[0] })); - $element.trigger($.Event("mousedown", { srcElement: $element[0] })); - - var evt = document.createEvent("MouseEvents"); - evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); - $element[0].dispatchEvent(evt); - - // trigger after for step like: mouseenter, next step click on button display with mouseenter - setTimeout(function () { - $element.trigger($.Event("mouseup", { srcElement: $element[0] })); - $element.trigger($.Event("mouseleave", { srcElement: $element[0] })); - }, 1000); } + }, + 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 () { + 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(T.timer); + clearTimeout(T.testtimer); + }; + + function checkNext () { + T.autoTogglePopover(); + + clearTimeout(T.timer); + if (T.check(next)) { + clearTimeout(T.currentTimer); + // use an other timeout for cke dom loading + setTimeout(function () { + T.nextStep(next); + }, T.defaultDelay); + } else if (!overlaps || new Date().getTime() - time < overlaps) { + T.timer = setTimeout(checkNext, T.defaultDelay); + } else { + T.error("Can't reach the next step"); + } + } + checkNext(); + }, + nextStep: function (step) { + var state = T.getState(); + + if (!state) { + return; + } + + step = step || state.step; + T.saveState(state.id, state.mode, step.id); + + T.autoTogglePopover(true); + + if (step.onload) { + step.onload(); + } + + var next = state.tour.steps[step.id+1]; + if (next) { + setTimeout(function () { + T.waitNextStep(); + if (state.mode === "test") { + setTimeout(function(){ + T.autoNextStep(state.tour, step); + }, T.defaultDelay); + } + }, next.wait || 0); + } else { + T.endTour(); + } + }, + endTour: function () { + 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 (tour, step) { + clearTimeout(T.testtimer); + + function autoStep () { + if (!step) return; + + if (step.autoComplete) { + step.autoComplete(tour); + } + + $(".popover.tour [data-role='next']").click(); + + var $element = $(step.element); + if (!$element.size()) return; + + if (step.snippet) { + + T.autoDragAndDropSnippet($element); + + } else if (step.sampleText) { + + $element.trigger($.Event("keydown", { srcElement: $element })); + if ($element.is("input") ) { + $element.val(step.sampleText); + } if ($element.is("select")) { + $element.find("[value='"+step.sampleText+"'], option:contains('"+step.sampleText+"')").attr("selected", true); + $element.val(step.sampleText); + } else { + $element.html(step.sampleText); + } + setTimeout(function () { + $element.trigger($.Event("keyup", { srcElement: $element })); + $element.trigger($.Event("change", { srcElement: $element })); + }, T.defaultDelay<<1); + + } else if ($element.is(":visible")) { + + $element.trigger($.Event("mouseenter", { srcElement: $element[0] })); + $element.trigger($.Event("mousedown", { srcElement: $element[0] })); + + var evt = document.createEvent("MouseEvents"); + evt.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); + $element[0].dispatchEvent(evt); + + // trigger after for step like: mouseenter, next step click on button display with mouseenter + setTimeout(function () { + $element.trigger($.Event("mouseup", { srcElement: $element[0] })); + $element.trigger($.Event("mouseleave", { srcElement: $element[0] })); + }, 1000); + } + } + T.testtimer = setTimeout(autoStep, 100); + }, + autoDragAndDropSnippet: function (selector) { + var $thumbnail = $(selector).first(); + var thumbnailPosition = $thumbnail.position(); + $thumbnail.trigger($.Event("mousedown", { which: 1, pageX: thumbnailPosition.left, pageY: thumbnailPosition.top })); + $thumbnail.trigger($.Event("mousemove", { which: 1, pageX: document.body.scrollWidth/2, pageY: document.body.scrollHeight/2 })); + var $dropZone = $(".oe_drop_zone").first(); + var dropPosition = $dropZone.position(); + $dropZone.trigger($.Event("mouseup", { which: 1, pageX: dropPosition.left, pageY: dropPosition.top })); } - T.testtimer = setTimeout(autoStep, 100); -}; -T.autoDragAndDropSnippet = function (selector) { - var $thumbnail = $(selector).first(); - var thumbnailPosition = $thumbnail.position(); - $thumbnail.trigger($.Event("mousedown", { which: 1, pageX: thumbnailPosition.left, pageY: thumbnailPosition.top })); - $thumbnail.trigger($.Event("mousemove", { which: 1, pageX: document.body.scrollWidth/2, pageY: document.body.scrollHeight/2 })); - var $dropZone = $(".oe_drop_zone").first(); - var dropPosition = $dropZone.position(); - $dropZone.trigger($.Event("mouseup", { which: 1, pageX: dropPosition.left, pageY: dropPosition.top })); }; //$(document).ready(T.running); From 24d37e284407a26987b1be3e539fa0243d81f656 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Fri, 21 Mar 2014 14:45:46 +0100 Subject: [PATCH 051/356] [FIX] website tour: template loading hide the real error bzr revid: chm@openerp.com-20140321134546-i5nw73en8thzrwc2 --- addons/website/static/src/js/website.tour.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index 6a5f8faab76..eed431e071b 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -286,10 +286,10 @@ var T = website.Tour = { } }, popoverTitle: function (tour, options) { - return openerp.qweb.render('website.tour_popover_title', options); + return openerp.qweb ? openerp.qweb.render('website.tour_popover_title', options) : options.title; }, popover: function (options) { - return openerp.qweb.render('website.tour_popover', options); + return openerp.qweb ? openerp.qweb.render('website.tour_popover', options) : options.title; }, getLang: function () { return $("html").attr("lang").replace(/-/, '_'); From 62814f9c2331340a5721331d652b0fdc0d2a201d Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Mon, 24 Mar 2014 09:11:12 +0100 Subject: [PATCH 052/356] [FIX] website: activate new tour tests bzr revid: chm@openerp.com-20140324081112-15n58puu29weydme --- addons/website/tests/test_ui.py | 2 +- addons/website_blog/tests/test_ui.py | 2 +- addons/website_event/tests/test_ui.py | 2 +- addons/website_event_sale/tests/test_ui.py | 6 +++--- addons/website_sale/tests/test_ui.py | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) 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_blog/tests/test_ui.py b/addons/website_blog/tests/test_ui.py index e99884ba4b1..35b7fdc56e7 100644 --- a/addons/website_blog/tests/test_ui.py +++ b/addons/website_blog/tests/test_ui.py @@ -2,5 +2,5 @@ import openerp.tests class TestUi(openerp.tests.HttpCase): def test_admin(self): - self.phantom_js("/", "openerp.website.Tour.run_test('blog')", "openerp.website.Tour") + self.phantom_js("/", "openerp.website.Tour.run('blog', 'test')", "openerp.website.Tour") diff --git a/addons/website_event/tests/test_ui.py b/addons/website_event/tests/test_ui.py index c4e884c0325..b59cdd94dcc 100644 --- a/addons/website_event/tests/test_ui.py +++ b/addons/website_event/tests/test_ui.py @@ -2,5 +2,5 @@ import openerp.tests class TestUi(openerp.tests.HttpCase): def test_admin(self): - self.phantom_js("/", "openerp.website.Tour.run_test('event')", "openerp.website.Tour") + self.phantom_js("/", "openerp.website.Tour.run('event', 'test')", "openerp.website.Tour") diff --git a/addons/website_event_sale/tests/test_ui.py b/addons/website_event_sale/tests/test_ui.py index b5950aaec54..b204b2bad69 100644 --- a/addons/website_event_sale/tests/test_ui.py +++ b/addons/website_event_sale/tests/test_ui.py @@ -9,11 +9,11 @@ inject = [ @openerp.tests.common.post_install(True) class TestUi(openerp.tests.HttpCase): def test_admin(self): - self.phantom_js("/", "openerp.website.Tour.run_test('event_buy_tickets')", "openerp.website.Tour", inject=inject) + self.phantom_js("/", "openerp.website.Tour.run('event_buy_tickets', 'test')", "openerp.website.Tour", inject=inject) def test_demo(self): - self.phantom_js("/", "openerp.website.Tour.run_test('event_buy_tickets')", "openerp.website.Tour", login="demo", password="demo", inject=inject); + self.phantom_js("/", "openerp.website.Tour.run('event_buy_tickets', 'test')", "openerp.website.Tour", login="demo", password="demo", inject=inject); def test_public(self): - self.phantom_js("/", "openerp.website.Tour.run_test('event_buy_tickets')", "openerp.website.Tour", login=None, inject=inject); + self.phantom_js("/", "openerp.website.Tour.run('event_buy_tickets', 'test')", "openerp.website.Tour", login=None, inject=inject); diff --git a/addons/website_sale/tests/test_ui.py b/addons/website_sale/tests/test_ui.py index 0eb5daae97a..0cedf2d0409 100644 --- a/addons/website_sale/tests/test_ui.py +++ b/addons/website_sale/tests/test_ui.py @@ -11,13 +11,13 @@ inject = [ @openerp.tests.common.post_install(True) class TestUi(openerp.tests.HttpCase): def test_01_admin_shop_tour(self): - self.phantom_js("/", "openerp.website.Tour.run_test('shop')", "openerp.website.Tour.Shop", login="admin") + self.phantom_js("/", "openerp.website.Tour.run('shop', 'test')", "openerp.website.Tour.Shop", login="admin") def test_02_admin_checkout(self): - self.phantom_js("/", "openerp.website.Tour.run_test('shop_buy_product')", "openerp.website.Tour.ShopTest", login="admin", inject=inject) + self.phantom_js("/", "openerp.website.Tour.run('shop_buy_product', 'test')", "openerp.website.Tour.ShopTest", login="admin", inject=inject) def test_03_demo_checkout(self): - self.phantom_js("/", "openerp.website.Tour.run_test('shop_buy_product')", "openerp.website.Tour.ShopTest", login="demo", inject=inject) + self.phantom_js("/", "openerp.website.Tour.run('shop_buy_product', 'test')", "openerp.website.Tour.ShopTest", login="demo", inject=inject) def test_04_public_checkout(self): - self.phantom_js("/", "openerp.website.Tour.run_test('shop_buy_product')", "openerp.website.Tour.ShopTest", inject=inject) + self.phantom_js("/", "openerp.website.Tour.run('shop_buy_product', 'test')", "openerp.website.Tour.ShopTest", inject=inject) From b28a32f6ef398347ed0a207762a75c98a2d5a5b1 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Wed, 26 Mar 2014 15:44:42 +0100 Subject: [PATCH 053/356] [FIX] website: test_ui running bzr revid: chm@openerp.com-20140326144442-ye2a51f03wyaxi8a --- addons/website_blog/tests/test_ui.py | 2 +- addons/website_event/tests/test_ui.py | 2 +- addons/website_event_sale/tests/test_ui.py | 6 +++--- addons/website_sale/tests/test_ui.py | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/addons/website_blog/tests/test_ui.py b/addons/website_blog/tests/test_ui.py index 35b7fdc56e7..b34ef25c002 100644 --- a/addons/website_blog/tests/test_ui.py +++ b/addons/website_blog/tests/test_ui.py @@ -2,5 +2,5 @@ import openerp.tests class TestUi(openerp.tests.HttpCase): def test_admin(self): - self.phantom_js("/", "openerp.website.Tour.run('blog', 'test')", "openerp.website.Tour") + self.phantom_js("/", "openerp.website.Tour.run('blog', 'test')", "openerp.website.Tour.tours.blog") diff --git a/addons/website_event/tests/test_ui.py b/addons/website_event/tests/test_ui.py index b59cdd94dcc..96a17a91563 100644 --- a/addons/website_event/tests/test_ui.py +++ b/addons/website_event/tests/test_ui.py @@ -2,5 +2,5 @@ import openerp.tests class TestUi(openerp.tests.HttpCase): def test_admin(self): - self.phantom_js("/", "openerp.website.Tour.run('event', 'test')", "openerp.website.Tour") + self.phantom_js("/", "openerp.website.Tour.run('event', 'test')", "openerp.website.Tour.tours.event") diff --git a/addons/website_event_sale/tests/test_ui.py b/addons/website_event_sale/tests/test_ui.py index b204b2bad69..3e26fba5cf4 100644 --- a/addons/website_event_sale/tests/test_ui.py +++ b/addons/website_event_sale/tests/test_ui.py @@ -9,11 +9,11 @@ inject = [ @openerp.tests.common.post_install(True) class TestUi(openerp.tests.HttpCase): def test_admin(self): - self.phantom_js("/", "openerp.website.Tour.run('event_buy_tickets', 'test')", "openerp.website.Tour", inject=inject) + self.phantom_js("/", "openerp.website.Tour.run('event_buy_tickets', 'test')", "openerp.website.Tour.tours.event_buy_tickets", inject=inject) def test_demo(self): - self.phantom_js("/", "openerp.website.Tour.run('event_buy_tickets', 'test')", "openerp.website.Tour", login="demo", password="demo", inject=inject); + self.phantom_js("/", "openerp.website.Tour.run('event_buy_tickets', 'test')", "openerp.website.Tour.tours.event_buy_tickets", login="demo", password="demo", inject=inject); def test_public(self): - self.phantom_js("/", "openerp.website.Tour.run('event_buy_tickets', 'test')", "openerp.website.Tour", login=None, inject=inject); + self.phantom_js("/", "openerp.website.Tour.run('event_buy_tickets', 'test')", "openerp.website.Tour.tours.event_buy_tickets", login=None, inject=inject); diff --git a/addons/website_sale/tests/test_ui.py b/addons/website_sale/tests/test_ui.py index 0cedf2d0409..61b0e904abf 100644 --- a/addons/website_sale/tests/test_ui.py +++ b/addons/website_sale/tests/test_ui.py @@ -4,20 +4,20 @@ import openerp.tests inject = [ ("openerp.website.Tour", os.path.join(os.path.dirname(__file__), '../../website/static/src/js/website.tour.js')), - ("openerp.website.Tour.ShopTest", os.path.join(os.path.dirname(__file__), "../static/src/js/website.tour.sale.js")), + ("openerp.website.Tour.tours.shop_buy_product", os.path.join(os.path.dirname(__file__), "../static/src/js/website.tour.sale.js")), ] @openerp.tests.common.at_install(False) @openerp.tests.common.post_install(True) class TestUi(openerp.tests.HttpCase): def test_01_admin_shop_tour(self): - self.phantom_js("/", "openerp.website.Tour.run('shop', 'test')", "openerp.website.Tour.Shop", login="admin") + self.phantom_js("/", "openerp.website.Tour.run('shop', 'test')", "openerp.website.Tour.tours.shop", login="admin") def test_02_admin_checkout(self): - self.phantom_js("/", "openerp.website.Tour.run('shop_buy_product', 'test')", "openerp.website.Tour.ShopTest", login="admin", inject=inject) + self.phantom_js("/", "openerp.website.Tour.run('shop_buy_product', 'test')", "openerp.website.Tour.tours.shop_buy_product", login="admin", inject=inject) def test_03_demo_checkout(self): - self.phantom_js("/", "openerp.website.Tour.run('shop_buy_product', 'test')", "openerp.website.Tour.ShopTest", login="demo", inject=inject) + self.phantom_js("/", "openerp.website.Tour.run('shop_buy_product', 'test')", "openerp.website.Tour.tours.shop_buy_product", login="demo", inject=inject) def test_04_public_checkout(self): - self.phantom_js("/", "openerp.website.Tour.run('shop_buy_product', 'test')", "openerp.website.Tour.ShopTest", inject=inject) + self.phantom_js("/", "openerp.website.Tour.run('shop_buy_product', 'test')", "openerp.website.Tour.tours.shop_buy_product", inject=inject) From fd593a3049407103165e4a872e92b9d39f641578 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Mon, 31 Mar 2014 17:12:37 +0200 Subject: [PATCH 054/356] [FIX] website_sale: test tour and activate website_event_sale test bzr revid: chm@openerp.com-20140331151237-ec0j7ywfsbjsw83k --- addons/website/static/src/js/website.tour.js | 17 +++++++++-------- addons/website_event_sale/tests/__init__.py | 2 +- addons/website_event_sale/tests/test_ui.py | 6 ++++-- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index eed431e071b..1a4a987739f 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -306,7 +306,7 @@ var T = website.Tour = { window.location.hash = ""; T.saveState(state.id, state.mode, state.step_id); } - if (!state.id) { + if (!state.id || !T.tours[state.id]) { return; } state.tour = T.tours[state.id]; @@ -349,20 +349,21 @@ var T = website.Tour = { clearTimeout(T.testtimer); T.closePopover(); }, + testRunning: 0, running: function () { - var state = T.getState(); - if (state) { - T.registerSteps(state.tour); + setTimeout(function () { if ($.ajaxBusy) { $(document).ajaxStop(function() { - setTimeout(function () { - T.nextStep(); - },0); + var state = T.getState(); + T.registerSteps(state.tour); + T.nextStep(); }); } else { + var state = T.getState(); + T.registerSteps(state.tour); T.nextStep(); } - } + },0); }, check: function (step) { return (step && diff --git a/addons/website_event_sale/tests/__init__.py b/addons/website_event_sale/tests/__init__.py index c9d4e3399ba..bf8ba6321ad 100644 --- a/addons/website_event_sale/tests/__init__.py +++ b/addons/website_event_sale/tests/__init__.py @@ -1 +1 @@ -#import test_ui +import test_ui diff --git a/addons/website_event_sale/tests/test_ui.py b/addons/website_event_sale/tests/test_ui.py index 3e26fba5cf4..fccbabb9d18 100644 --- a/addons/website_event_sale/tests/test_ui.py +++ b/addons/website_event_sale/tests/test_ui.py @@ -1,8 +1,10 @@ +import os + import openerp.tests inject = [ - "./../../../website/static/src/js/website.tour.test.js", - "./../../../website_event_sale/static/src/js/website.tour.event_sale.js", + ("openerp.website.Tour", os.path.join(os.path.dirname(__file__), '../../website/static/src/js/website.tour.js')), + ("openerp.website.Tour.tours.event_buy_tickets", os.path.join(os.path.dirname(__file__), "../static/src/js/website.tour.event_sale.js")), ] @openerp.tests.common.at_install(False) From 055cae21512a7af32f1a67ed0460c11477679711 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 2 Apr 2014 13:27:09 +0200 Subject: [PATCH 055/356] [WIP] load script bzr revid: fme@openerp.com-20140402112709-vbm7p35s980p0s2u --- addons/web/static/src/js/openerpframework.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/addons/web/static/src/js/openerpframework.js b/addons/web/static/src/js/openerpframework.js index 5dcc2197944..8a2961e058f 100644 --- a/addons/web/static/src/js/openerpframework.js +++ b/addons/web/static/src/js/openerpframework.js @@ -1477,6 +1477,26 @@ openerp.time_to_str = function(obj) { + lpad(obj.getSeconds(),2); }; +openerp.load_script = function (url) { + var def = $.Deferred(); + if (document.getElementById(url)) { + def.resolve(); + } else { + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.src = script.id = url; + script.onload = function () { + def.resolve(); + }; + script.onerror = function () { + console.error("Error loading file", url); + def.reject(); + }; + document.head.appendChild(script); + } + return def; +}; + openerp.declare = declare; return openerp; From cbb5488f978ae592914fa56a2378cd54aa1bd00c Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 8 Apr 2014 09:52:05 +0200 Subject: [PATCH 056/356] [ADD] t-call-assets bzr revid: fme@openerp.com-20140408075205-9v8fxg786jj0fj77 --- openerp/addons/base/ir/ir_qweb.py | 40 +++++++++++++++++++++++++++ openerp/http.py | 46 +++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/openerp/addons/base/ir/ir_qweb.py b/openerp/addons/base/ir/ir_qweb.py index 3c195c93c6c..391dbf610e9 100644 --- a/openerp/addons/base/ir/ir_qweb.py +++ b/openerp/addons/base/ir/ir_qweb.py @@ -8,12 +8,14 @@ import math import re import sys import xml # FIXME use lxml and etree +import lxml import babel import babel.dates import werkzeug.utils from PIL import Image +import openerp.http import openerp.tools from openerp.tools.safe_eval import safe_eval as eval from openerp.osv import osv, orm, fields @@ -385,6 +387,44 @@ class QWeb(orm.AbstractModel): return self.render(cr, uid, self.eval_format(template_attributes["call"], d), d) + def render_tag_call_assets(self, element, template_attributes, generated_attributes, qwebcontext): + template_attributes['call'] = template_attributes['call-assets'] + content = self.render_tag_call(element, template_attributes, generated_attributes, qwebcontext) + html = lxml.html.fragment_fromstring('%s' % content) # avoid this hack if possible + + tags = dict() + current_style = None + current_script = None + + for el in html: + if el.tag == 'link' and el.attrib.get('rel') == 'stylesheet': + cssfile = el.attrib.get('href') + if current_style is None: + current_style = el + tags[current_style] = [] + el.attrib['href'] = '/web/css/' + else: + html.remove(el) + tags[current_style].append(cssfile) + if el.tag == 'script': + if 'src' not in el.attrib: + current_script = None + else: + jsfile = el.attrib['src'] + if current_script is None: + current_script = el + tags[current_script] = [] + el.attrib['src'] = '/web/js/' + else: + html.remove(el) + tags[current_script].append(jsfile) + + for tag, files in tags.items(): + attr = 'href' if 'href' in tag.attrib else 'src' + tag.attrib[attr] += openerp.http.pathlist_to_base64(files) + r = lxml.html.tostring(html)[3:-4] # avoid this hack if possible + return r + def render_tag_set(self, element, template_attributes, generated_attributes, qwebcontext): if "value" in template_attributes: qwebcontext[template_attributes["set"]] = self.eval_object(template_attributes["value"], qwebcontext) diff --git a/openerp/http.py b/openerp/http.py index 2e21d27528a..ccff43ed271 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -3,6 +3,7 @@ # OpenERP HTTP layer #---------------------------------------------------------- import ast +import base64 import collections import contextlib import errno @@ -137,6 +138,51 @@ def redirect_with_hash(url, code=303): return werkzeug.utils.redirect(url, code) return "" % url +def pathlist_to_base64(files): + """ Serialize a path list to an url-safe string trying to avoid path + duplication while keeping files order. + """ + seen = [] + concat = [] + current_path = None + for f in files: + if f in seen: + continue + seen.append(f) + path, fname = os.path.split(f) + if not path: + path = '/' + if path != current_path: + current_path = path + concat.append(path) + concat.append(fname) + r = '|'.join(concat) + return base64.urlsafe_b64encode(r.encode('zlib')) + +def base64_to_pathlist(s, fspath=False): + """ Deserialize a string generated by filelist_to_base64 + + :param fspath: returns tuple(filesystem path, url path) if True + """ + files = [] + decoded = base64.urlsafe_b64decode(str(s)).decode('zlib') + items = decoded.split('|') + current_path = '/' + while items: + item = items.pop(0) + if item.startswith('/'): + current_path = item + else: + path = current_path + '/' + item + if fspath: + module = filter(bool, path.split('/'))[0] + mpath = addons_manifest[module]['addons_path'] + fs_path = mpath + path.replace('/', os.path.sep) + files.append((fs_path, path)) + else: + files.append(path) + return files + class WebRequest(object): """ Parent class for all OpenERP Web request types, mostly deals with initialization and setup of the request object (the dispatching itself has From f4225eda24d7ccd8485c778d917b43409e8e64fa Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Tue, 8 Apr 2014 14:41:19 +0200 Subject: [PATCH 057/356] [FIX] website test tour bzr revid: chm@openerp.com-20140408124119-jkmeh5vfdb3sjlna --- addons/website/static/src/js/website.tour.js | 19 ++++++++++--------- .../static/src/js/website.tour.event_sale.js | 4 ++-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index 1a4a987739f..02befe4fdbb 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -351,18 +351,19 @@ var T = website.Tour = { }, testRunning: 0, running: function () { - setTimeout(function () { - if ($.ajaxBusy) { - $(document).ajaxStop(function() { - var state = T.getState(); - T.registerSteps(state.tour); - T.nextStep(); - }); - } else { - var state = T.getState(); + function run () { + var state = T.getState(); + if (state) { T.registerSteps(state.tour); T.nextStep(); } + } + setTimeout(function () { + if ($.ajaxBusy) { + $(document).ajaxStop(run); + } else { + run(); + } },0); }, check: function (step) { diff --git a/addons/website_event_sale/static/src/js/website.tour.event_sale.js b/addons/website_event_sale/static/src/js/website.tour.event_sale.js index 668a05001af..97de098240e 100644 --- a/addons/website_event_sale/static/src/js/website.tour.event_sale.js +++ b/addons/website_event_sale/static/src/js/website.tour.event_sale.js @@ -57,11 +57,11 @@ }, { title: "select payment", - element: '#payment_method label:has(img[title="transfer"]) input', + element: '#payment_method label:has(img[title="Wire Transfer"]) input', }, { title: "Pay Now", - waitFor: '#payment_method label:has(input:checked):has(img[title="transfer"])', + waitFor: '#payment_method label:has(input:checked):has(img[title="Wire Transfer"])', element: '.oe_sale_acquirer_button .btn[name="submit"]:visible', }, { From 69dfcab5d9c9b461df49d297b5fa2f8d5539de90 Mon Sep 17 00:00:00 2001 From: "chm@openerp.com" <> Date: Tue, 8 Apr 2014 16:47:41 +0200 Subject: [PATCH 058/356] [WIP] add console.log bzr revid: chm@openerp.com-20140408144741-ylt01232o58xb7ys --- .../static/src/js/website.tour.event_sale.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/addons/website_event_sale/static/src/js/website.tour.event_sale.js b/addons/website_event_sale/static/src/js/website.tour.event_sale.js index 97de098240e..75ad2297f6b 100644 --- a/addons/website_event_sale/static/src/js/website.tour.event_sale.js +++ b/addons/website_event_sale/static/src/js/website.tour.event_sale.js @@ -18,6 +18,11 @@ waitNot: 'a[href*="/event"]:contains("Functional Webinar")', autoComplete: function () { // use onload if website_event_track is installed + console.log("--------------------------------------------"); + console.log($('form:contains("Ticket Type")').size()); + console.log("--------------------------------------------"); + console.log($('a[href*="/event"][href*="/register"]').attr("href")); + console.log("--------------------------------------------"); if (!$('form:contains("Ticket Type")').size()) { window.location.href = $('a[href*="/event"][href*="/register"]').attr("href"); } From 8e14e7a870b04345e7ae101910b1dc97c1865dca Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Wed, 9 Apr 2014 18:16:48 +0200 Subject: [PATCH 059/356] [WIP] AssetsBundle bzr revid: fme@openerp.com-20140409161648-sx87vc0saj30f68x --- addons/web/controllers/main.py | 52 ++++++++++-------------- addons/web/static/src/js/chrome.js | 2 +- addons/web/views/webclient_templates.xml | 24 +++++++---- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 2771c658270..4517c919b4c 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -31,6 +31,7 @@ except ImportError: import openerp import openerp.modules.registry +from openerp.addons.base.ir.ir_qweb import AssetsBundle from openerp.tools.translate import _ from openerp import http @@ -364,30 +365,17 @@ def manifest_glob(extension, addons=None, db=None, include_remotes=False): r.append((None, pattern)) else: for path in glob.glob(os.path.normpath(os.path.join(addons_path, addon, pattern))): - # Hack for IE, who limit 288Ko, 4095 rules, 31 sheets - # http://support.microsoft.com/kb/262161/en - if pattern == "static/lib/bootstrap/css/bootstrap.css": - if include_remotes: - r.insert(0, (None, fs2web(path[len(addons_path):]))) - else: - r.append((path, fs2web(path[len(addons_path):]))) + r.append((path, fs2web(path[len(addons_path):]))) return r -def manifest_list(extension, mods=None, db=None, debug=False): +def manifest_list(extension, mods=None, db=None, debug=None): """ list ressources to load specifying either: mods: a comma separated string listing modules db: a database name (return all installed modules in that database) """ + if debug is not None: + _logger.warning("openerp.addons.web.main.manifest_list(): debug parameter is deprecated") files = manifest_glob(extension, addons=mods, db=db, include_remotes=True) - if not debug: - path = '/web/webclient/' + extension - if mods is not None: - path += '?' + werkzeug.url_encode({'mods': mods}) - elif db: - path += '?' + werkzeug.url_encode({'db': db}) - - remotes = [wp for fp, wp in files if fp is None] - return [path] + remotes return [wp for _fp, wp in files] def get_last_modified(files): @@ -614,26 +602,16 @@ html_template = """ """ -def render_bootstrap_template(template, values=None, debug=False, db=None, **kw): - if not db: - db = request.db - if request.debug: - debug = True +def render_bootstrap_template(template, values=None, **kw): if values is None: - values = {} - values['debug'] = debug - values['current_db'] = db + values = dict() try: values['databases'] = http.db_list() except openerp.exceptions.AccessDenied: values['databases'] = None - for res in ['js', 'css']: - if res not in values: - values[res] = manifest_list(res, db=db, debug=debug) - if 'modules' not in values: - values['modules'] = module_boot(db=db) + values['modules'] = module_boot() values['modules'] = simplejson.dumps(values['modules']) return request.render(template, values, **kw) @@ -679,6 +657,20 @@ class Home(http.Controller): def login(self, db, login, key, redirect="/web", **kw): return login_and_redirect(db, login, key, redirect_url=redirect) + @http.route('/web/js/', type='http', auth="none") + def js_bundle(self, xmlid, **kw): + values = dict(manifest_list=manifest_list) + html = request.render(xmlid, lazy=False, qcontext=values) + bundle = AssetsBundle(xmlid, html) + return bundle.js() + + @http.route('/web/css/', type='http', auth='none') + def css_bundle(self, xmlid, **kw): + values = dict(manifest_list=manifest_list) + html = request.render(xmlid, lazy=False, qcontext=values) + bundle = AssetsBundle(xmlid, html) + return bundle.css() + class WebClient(http.Controller): @http.route('/web/webclient/csslist', type='json', auth="none") diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 8e5df84e2c3..ae1499549ba 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -1478,7 +1478,7 @@ instance.web.embed = function (origin, dbname, login, key, action, options) { $('head').append($('', { 'rel': 'stylesheet', 'type': 'text/css', - 'href': origin +'/web/webclient/css' + 'href': origin +'/web/css/web.assets_webclient' })); var currentScript = document.currentScript; if (!currentScript) { diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 46bae5f83ca..a0c6cc8463b 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -3,6 +3,19 @@ --> + + +