���ѧۧݧ�ӧ�� �ާ֧ߧ֧էا֧� - ���֧էѧܧ�ڧ��ӧѧ�� - /home3/cpr76684/public_html/amd.tar
���ѧ٧ѧ�
build/workshopview.min.js 0000644 00000001403 15152047724 0011537 0 ustar 00 /** * Sets the equal height to the user plan widget boxes. * * @module mod_workshop/workshopview * @category output * @copyright Loc Nguyen <loc.nguyendinh@harveynash.vn> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define("mod_workshop/workshopview",["jquery"],(function($){function equalHeight(group){var tallest=0;group.height("auto"),group.each((function(){var thisHeight=$(this).height();thisHeight>tallest&&(tallest=thisHeight)})),group.height(tallest)}return{init:function(){var $dt=$(".path-mod-workshop .userplan dt"),$dd=$(".path-mod-workshop .userplan dd");equalHeight($dt),equalHeight($dd),$(window).on("resize",(function(){equalHeight($dt),equalHeight($dd)}))}}})); //# sourceMappingURL=workshopview.min.js.map build/modform.min.js.map 0000644 00000012605 15152047724 0011215 0 ustar 00 {"version":3,"file":"modform.min.js","sources":["../src/modform.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Additional javascript for the Workshop module form.\n *\n * @module mod_workshop/modform\n * @copyright The Open University 2018\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'], function($) {\n\n var submissionTypes = {\n text: {\n available: null,\n required: null,\n requiredHidden: null\n },\n file: {\n available: null,\n required: null,\n requiredHidden: null\n }\n };\n\n /**\n * Determine whether one of the submission types has been marked as not available.\n *\n * If it has been marked not available, clear and disable its required checkbox. Then determine if the other submission\n * type is available, and if it is, check and disable its required checkbox.\n *\n * @param {Object} checkUnavailable\n * @param {Object} checkAvailable\n */\n function checkAvailability(checkUnavailable, checkAvailable) {\n if (!checkUnavailable.available.prop('checked')) {\n checkUnavailable.required.prop('disabled', true);\n checkUnavailable.required.prop('checked', false);\n if (checkAvailable.available.prop('checked')) {\n checkAvailable.required.prop('disabled', true);\n checkAvailable.required.prop('checked', true);\n // Also set the checkbox's hidden field to 1 so a 'required' value is submitted for the submission type.\n checkAvailable.requiredHidden.val(1);\n }\n }\n }\n\n /**\n * Enable the submission type's required checkbox and uncheck it.\n *\n * @param {Object} submissionType\n */\n function enableRequired(submissionType) {\n submissionType.required.prop('disabled', false);\n submissionType.required.prop('checked', false);\n submissionType.requiredHidden.val(0);\n }\n\n /**\n * Check which submission types have been marked as available, and disable required checkboxes as necessary.\n */\n function submissionTypeChanged() {\n checkAvailability(submissionTypes.file, submissionTypes.text);\n checkAvailability(submissionTypes.text, submissionTypes.file);\n if (submissionTypes.text.available.prop('checked') && submissionTypes.file.available.prop('checked')) {\n enableRequired(submissionTypes.text);\n enableRequired(submissionTypes.file);\n }\n }\n\n return /** @alias module:mod_workshop/modform */ {\n /**\n * Find all the required fields, set up event listeners, and set the initial state of required checkboxes.\n */\n init: function() {\n submissionTypes.text.available = $('#id_submissiontypetextavailable');\n submissionTypes.text.required = $('#id_submissiontypetextrequired');\n submissionTypes.text.requiredHidden = $('input[name=\"submissiontypetextrequired\"][type=\"hidden\"]');\n submissionTypes.file.available = $('#id_submissiontypefileavailable');\n submissionTypes.file.required = $('#id_submissiontypefilerequired');\n submissionTypes.file.requiredHidden = $('input[name=\"submissiontypefilerequired\"][type=\"hidden\"]');\n submissionTypes.text.available.on('change', submissionTypeChanged);\n submissionTypes.file.available.on('change', submissionTypeChanged);\n submissionTypeChanged();\n }\n };\n});\n"],"names":["define","$","submissionTypes","text","available","required","requiredHidden","file","checkAvailability","checkUnavailable","checkAvailable","prop","val","enableRequired","submissionType","submissionTypeChanged","init","on"],"mappings":";;;;;;;AAsBAA,8BAAO,CAAC,WAAW,SAASC,OAEpBC,gBAAkB,CAClBC,KAAM,CACFC,UAAW,KACXC,SAAU,KACVC,eAAgB,MAEpBC,KAAM,CACFH,UAAW,KACXC,SAAU,KACVC,eAAgB,gBAafE,kBAAkBC,iBAAkBC,gBACpCD,iBAAiBL,UAAUO,KAAK,aACjCF,iBAAiBJ,SAASM,KAAK,YAAY,GAC3CF,iBAAiBJ,SAASM,KAAK,WAAW,GACtCD,eAAeN,UAAUO,KAAK,aAC9BD,eAAeL,SAASM,KAAK,YAAY,GACzCD,eAAeL,SAASM,KAAK,WAAW,GAExCD,eAAeJ,eAAeM,IAAI,cAUrCC,eAAeC,gBACpBA,eAAeT,SAASM,KAAK,YAAY,GACzCG,eAAeT,SAASM,KAAK,WAAW,GACxCG,eAAeR,eAAeM,IAAI,YAM7BG,wBACLP,kBAAkBN,gBAAgBK,KAAML,gBAAgBC,MACxDK,kBAAkBN,gBAAgBC,KAAMD,gBAAgBK,MACpDL,gBAAgBC,KAAKC,UAAUO,KAAK,YAAcT,gBAAgBK,KAAKH,UAAUO,KAAK,aACtFE,eAAeX,gBAAgBC,MAC/BU,eAAeX,gBAAgBK,aAIU,CAI7CS,KAAM,WACFd,gBAAgBC,KAAKC,UAAYH,EAAE,mCACnCC,gBAAgBC,KAAKE,SAAWJ,EAAE,kCAClCC,gBAAgBC,KAAKG,eAAiBL,EAAE,2DACxCC,gBAAgBK,KAAKH,UAAYH,EAAE,mCACnCC,gBAAgBK,KAAKF,SAAWJ,EAAE,kCAClCC,gBAAgBK,KAAKD,eAAiBL,EAAE,2DACxCC,gBAAgBC,KAAKC,UAAUa,GAAG,SAAUF,uBAC5Cb,gBAAgBK,KAAKH,UAAUa,GAAG,SAAUF,uBAC5CA"} build/modform.min.js 0000644 00000003634 15152047724 0010443 0 ustar 00 /** * Additional javascript for the Workshop module form. * * @module mod_workshop/modform * @copyright The Open University 2018 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define("mod_workshop/modform",["jquery"],(function($){var submissionTypes={text:{available:null,required:null,requiredHidden:null},file:{available:null,required:null,requiredHidden:null}};function checkAvailability(checkUnavailable,checkAvailable){checkUnavailable.available.prop("checked")||(checkUnavailable.required.prop("disabled",!0),checkUnavailable.required.prop("checked",!1),checkAvailable.available.prop("checked")&&(checkAvailable.required.prop("disabled",!0),checkAvailable.required.prop("checked",!0),checkAvailable.requiredHidden.val(1)))}function enableRequired(submissionType){submissionType.required.prop("disabled",!1),submissionType.required.prop("checked",!1),submissionType.requiredHidden.val(0)}function submissionTypeChanged(){checkAvailability(submissionTypes.file,submissionTypes.text),checkAvailability(submissionTypes.text,submissionTypes.file),submissionTypes.text.available.prop("checked")&&submissionTypes.file.available.prop("checked")&&(enableRequired(submissionTypes.text),enableRequired(submissionTypes.file))}return{init:function(){submissionTypes.text.available=$("#id_submissiontypetextavailable"),submissionTypes.text.required=$("#id_submissiontypetextrequired"),submissionTypes.text.requiredHidden=$('input[name="submissiontypetextrequired"][type="hidden"]'),submissionTypes.file.available=$("#id_submissiontypefileavailable"),submissionTypes.file.required=$("#id_submissiontypefilerequired"),submissionTypes.file.requiredHidden=$('input[name="submissiontypefilerequired"][type="hidden"]'),submissionTypes.text.available.on("change",submissionTypeChanged),submissionTypes.file.available.on("change",submissionTypeChanged),submissionTypeChanged()}}})); //# sourceMappingURL=modform.min.js.map build/workshopview.min.js.map 0000644 00000004571 15152047724 0012324 0 ustar 00 {"version":3,"file":"workshopview.min.js","sources":["../src/workshopview.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Sets the equal height to the user plan widget boxes.\n *\n * @module mod_workshop/workshopview\n * @category output\n * @copyright Loc Nguyen <loc.nguyendinh@harveynash.vn>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery'], function($) {\n\n /**\n * Sets the equal height to all elements in the group.\n *\n * @param {jQuery} group List of nodes.\n */\n function equalHeight(group) {\n var tallest = 0;\n group.height('auto');\n group.each(function() {\n var thisHeight = $(this).height();\n if (thisHeight > tallest) {\n tallest = thisHeight;\n }\n });\n group.height(tallest);\n }\n\n return /** @alias module:mod_workshop/workshopview */ {\n init: function() {\n var $dt = $('.path-mod-workshop .userplan dt');\n var $dd = $('.path-mod-workshop .userplan dd');\n equalHeight($dt);\n equalHeight($dd);\n $(window).on(\"resize\", function() {\n equalHeight($dt);\n equalHeight($dd);\n });\n }\n };\n});\n"],"names":["define","$","equalHeight","group","tallest","height","each","thisHeight","this","init","$dt","$dd","window","on"],"mappings":";;;;;;;;AAuBAA,mCAAO,CAAC,WAAW,SAASC,YAOfC,YAAYC,WACbC,QAAU,EACdD,MAAME,OAAO,QACbF,MAAMG,MAAK,eACHC,WAAaN,EAAEO,MAAMH,SACrBE,WAAaH,UACbA,QAAUG,eAGlBJ,MAAME,OAAOD,eAGqC,CAClDK,KAAM,eACEC,IAAMT,EAAE,mCACRU,IAAMV,EAAE,mCACZC,YAAYQ,KACZR,YAAYS,KACZV,EAAEW,QAAQC,GAAG,UAAU,WACnBX,YAAYQ,KACZR,YAAYS"} src/workshopview.js 0000644 00000003455 15152047724 0010456 0 ustar 00 // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Sets the equal height to the user plan widget boxes. * * @module mod_workshop/workshopview * @category output * @copyright Loc Nguyen <loc.nguyendinh@harveynash.vn> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define(['jquery'], function($) { /** * Sets the equal height to all elements in the group. * * @param {jQuery} group List of nodes. */ function equalHeight(group) { var tallest = 0; group.height('auto'); group.each(function() { var thisHeight = $(this).height(); if (thisHeight > tallest) { tallest = thisHeight; } }); group.height(tallest); } return /** @alias module:mod_workshop/workshopview */ { init: function() { var $dt = $('.path-mod-workshop .userplan dt'); var $dd = $('.path-mod-workshop .userplan dd'); equalHeight($dt); equalHeight($dd); $(window).on("resize", function() { equalHeight($dt); equalHeight($dd); }); } }; }); src/modform.js 0000644 00000010031 15152047724 0007336 0 ustar 00 // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Additional javascript for the Workshop module form. * * @module mod_workshop/modform * @copyright The Open University 2018 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define(['jquery'], function($) { var submissionTypes = { text: { available: null, required: null, requiredHidden: null }, file: { available: null, required: null, requiredHidden: null } }; /** * Determine whether one of the submission types has been marked as not available. * * If it has been marked not available, clear and disable its required checkbox. Then determine if the other submission * type is available, and if it is, check and disable its required checkbox. * * @param {Object} checkUnavailable * @param {Object} checkAvailable */ function checkAvailability(checkUnavailable, checkAvailable) { if (!checkUnavailable.available.prop('checked')) { checkUnavailable.required.prop('disabled', true); checkUnavailable.required.prop('checked', false); if (checkAvailable.available.prop('checked')) { checkAvailable.required.prop('disabled', true); checkAvailable.required.prop('checked', true); // Also set the checkbox's hidden field to 1 so a 'required' value is submitted for the submission type. checkAvailable.requiredHidden.val(1); } } } /** * Enable the submission type's required checkbox and uncheck it. * * @param {Object} submissionType */ function enableRequired(submissionType) { submissionType.required.prop('disabled', false); submissionType.required.prop('checked', false); submissionType.requiredHidden.val(0); } /** * Check which submission types have been marked as available, and disable required checkboxes as necessary. */ function submissionTypeChanged() { checkAvailability(submissionTypes.file, submissionTypes.text); checkAvailability(submissionTypes.text, submissionTypes.file); if (submissionTypes.text.available.prop('checked') && submissionTypes.file.available.prop('checked')) { enableRequired(submissionTypes.text); enableRequired(submissionTypes.file); } } return /** @alias module:mod_workshop/modform */ { /** * Find all the required fields, set up event listeners, and set the initial state of required checkboxes. */ init: function() { submissionTypes.text.available = $('#id_submissiontypetextavailable'); submissionTypes.text.required = $('#id_submissiontypetextrequired'); submissionTypes.text.requiredHidden = $('input[name="submissiontypetextrequired"][type="hidden"]'); submissionTypes.file.available = $('#id_submissiontypefileavailable'); submissionTypes.file.required = $('#id_submissiontypefilerequired'); submissionTypes.file.requiredHidden = $('input[name="submissiontypefilerequired"][type="hidden"]'); submissionTypes.text.available.on('change', submissionTypeChanged); submissionTypes.file.available.on('change', submissionTypeChanged); submissionTypeChanged(); } }; }); build/events.min.js 0000644 00000001003 15152167036 0010267 0 ustar 00 define("tool_usertours/events",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.eventTypes=void 0;_exports.eventTypes={stepRender:"tool_usertours/stepRender",stepRendered:"tool_usertours/stepRendered",tourStart:"tool_usertours/tourStart",tourStarted:"tool_usertours/tourStarted",tourEnd:"tool_usertours/tourEnd",tourEnded:"tool_usertours/tourEnded",stepHide:"tool_usertours/stepHide",stepHidden:"tool_usertours/stepHidden"}})); //# sourceMappingURL=events.min.js.map build/events.min.js.map 0000644 00000011572 15152167036 0011057 0 ustar 00 {"version":3,"file":"events.min.js","sources":["../src/events.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript events for the `tool_usertours` subsystem.\n *\n * @module tool_usertours/events\n * @copyright 2021 Andrew Lyons <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n *\n * @example <caption>Example of listening to a step rendering event and cancelling it.</caption>\n * import {eventTypes as userTourEvents} from 'tool_usertours/events';\n *\n * document.addEventListener(userTourEvents.stepRender, e => {\n * console.log(e.detail.tour); // The Tour instance\n * e.preventDefault();\n * });\n */\n\n/**\n * Events for the component.\n *\n * @constant\n * @property {object} eventTypes\n * @property {String} eventTypes.stepRender See {@link event:tool_usertours/stepRender}\n * @property {String} eventTypes.stepRendered See {@link event:tool_usertours/stepRendered}\n * @property {String} eventTypes.tourStart See {@link event:tool_usertours/tourStart}\n * @property {String} eventTypes.tourStarted See {@link event:tool_usertours/tourStarted}\n * @property {String} eventTypes.tourEnd See {@link event:tool_usertours/tourEnd}\n * @property {String} eventTypes.tourEnded See {@link event:tool_usertours/tourEnded}\n * @property {String} eventTypes.stepHide See {@link event:tool_usertours/stepHide}\n * @property {String} eventTypes.stepHidden See {@link event:tool_usertours/stepHidden}\n */\nexport const eventTypes = {\n /**\n * An event triggered before a user tour step is rendered.\n *\n * This event is cancellable.\n *\n * @event tool_usertours/stepRender\n * @type {CustomEvent}\n * @property {object} detail\n * @property {tool_usertours/tour} detail.tour\n * @property {object} detail.stepConfig\n */\n stepRender: 'tool_usertours/stepRender',\n\n /**\n * An event triggered after a user tour step has been rendered.\n *\n * @event tool_usertours/stepRendered\n * @type {CustomEvent}\n * @property {object} detail\n * @property {tool_usertours/tour} detail.tour\n * @property {object} detail.stepConfig\n */\n stepRendered: 'tool_usertours/stepRendered',\n\n /**\n * An event triggered before a user tour starts.\n *\n * This event is cancellable.\n *\n * @event tool_usertours/tourStart\n * @type {CustomEvent}\n * @property {object} detail\n * @property {tool_usertours/tour} detail.tour\n * @property {Number} detail.startAt\n */\n tourStart: 'tool_usertours/tourStart',\n\n /**\n * An event triggered after a user tour has started.\n *\n * @event tool_usertours/tourStarted\n * @property {object} detail\n * @property {tool_usertours/tour} detail.tour\n * @type {CustomEvent}\n */\n tourStarted: 'tool_usertours/tourStarted',\n\n /**\n * An event triggered before a tour ends.\n *\n * This event is cancellable.\n *\n * @event tool_usertours/tourEnd\n * @property {object} detail\n * @property {tool_usertours/tour} detail.tour\n * @type {CustomEvent}\n */\n tourEnd: 'tool_usertours/tourEnd',\n\n /**\n * An event triggered after a tour has ended.\n *\n * @event tool_usertours/tourEnded\n * @property {object} detail\n * @property {tool_usertours/tour} detail.tour\n * @type {CustomEvent}\n */\n tourEnded: 'tool_usertours/tourEnded',\n\n /**\n * An event triggered before a step is hidden.\n *\n * This event is cancellable.\n *\n * @event tool_usertours/stepHide\n * @property {object} detail\n * @property {tool_usertours/tour} detail.tour\n * @type {CustomEvent}\n */\n stepHide: 'tool_usertours/stepHide',\n\n /**\n * An event triggered after a step has been hidden.\n *\n * @event tool_usertours/stepHidden\n * @property {object} detail\n * @property {tool_usertours/tour} detail.tour\n * @type {CustomEvent}\n */\n stepHidden: 'tool_usertours/stepHidden',\n};\n"],"names":["stepRender","stepRendered","tourStart","tourStarted","tourEnd","tourEnded","stepHide","stepHidden"],"mappings":"sKA6C0B,CAYtBA,WAAY,4BAWZC,aAAc,8BAadC,UAAW,2BAUXC,YAAa,6BAYbC,QAAS,yBAUTC,UAAW,2BAYXC,SAAU,0BAUVC,WAAY"} src/events.js 0000644 00000010665 15152167036 0007213 0 ustar 00 // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * Javascript events for the `tool_usertours` subsystem. * * @module tool_usertours/events * @copyright 2021 Andrew Lyons <andrew@nicols.co.uk> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * * @example <caption>Example of listening to a step rendering event and cancelling it.</caption> * import {eventTypes as userTourEvents} from 'tool_usertours/events'; * * document.addEventListener(userTourEvents.stepRender, e => { * console.log(e.detail.tour); // The Tour instance * e.preventDefault(); * }); */ /** * Events for the component. * * @constant * @property {object} eventTypes * @property {String} eventTypes.stepRender See {@link event:tool_usertours/stepRender} * @property {String} eventTypes.stepRendered See {@link event:tool_usertours/stepRendered} * @property {String} eventTypes.tourStart See {@link event:tool_usertours/tourStart} * @property {String} eventTypes.tourStarted See {@link event:tool_usertours/tourStarted} * @property {String} eventTypes.tourEnd See {@link event:tool_usertours/tourEnd} * @property {String} eventTypes.tourEnded See {@link event:tool_usertours/tourEnded} * @property {String} eventTypes.stepHide See {@link event:tool_usertours/stepHide} * @property {String} eventTypes.stepHidden See {@link event:tool_usertours/stepHidden} */ export const eventTypes = { /** * An event triggered before a user tour step is rendered. * * This event is cancellable. * * @event tool_usertours/stepRender * @type {CustomEvent} * @property {object} detail * @property {tool_usertours/tour} detail.tour * @property {object} detail.stepConfig */ stepRender: 'tool_usertours/stepRender', /** * An event triggered after a user tour step has been rendered. * * @event tool_usertours/stepRendered * @type {CustomEvent} * @property {object} detail * @property {tool_usertours/tour} detail.tour * @property {object} detail.stepConfig */ stepRendered: 'tool_usertours/stepRendered', /** * An event triggered before a user tour starts. * * This event is cancellable. * * @event tool_usertours/tourStart * @type {CustomEvent} * @property {object} detail * @property {tool_usertours/tour} detail.tour * @property {Number} detail.startAt */ tourStart: 'tool_usertours/tourStart', /** * An event triggered after a user tour has started. * * @event tool_usertours/tourStarted * @property {object} detail * @property {tool_usertours/tour} detail.tour * @type {CustomEvent} */ tourStarted: 'tool_usertours/tourStarted', /** * An event triggered before a tour ends. * * This event is cancellable. * * @event tool_usertours/tourEnd * @property {object} detail * @property {tool_usertours/tour} detail.tour * @type {CustomEvent} */ tourEnd: 'tool_usertours/tourEnd', /** * An event triggered after a tour has ended. * * @event tool_usertours/tourEnded * @property {object} detail * @property {tool_usertours/tour} detail.tour * @type {CustomEvent} */ tourEnded: 'tool_usertours/tourEnded', /** * An event triggered before a step is hidden. * * This event is cancellable. * * @event tool_usertours/stepHide * @property {object} detail * @property {tool_usertours/tour} detail.tour * @type {CustomEvent} */ stepHide: 'tool_usertours/stepHide', /** * An event triggered after a step has been hidden. * * @event tool_usertours/stepHidden * @property {object} detail * @property {tool_usertours/tour} detail.tour * @type {CustomEvent} */ stepHidden: 'tool_usertours/stepHidden', }; build/steps.min.js 0000644 00000003262 15152170055 0010125 0 ustar 00 define("tool_behat/steps",["exports","core/ajax","core/templates","core/pending"],(function(_exports,_ajax,_templates,_pending){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}} /** * Enhancements for the step definitions page. * * @module tool_behat/steps * @copyright 2022 Catalyst IT EU * @author Mark Johnson <mark.johnson@catalyst-eu.net> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_ajax=_interopRequireDefault(_ajax),_templates=_interopRequireDefault(_templates),_pending=_interopRequireDefault(_pending);_exports.init=()=>{document.addEventListener("change",(async e=>{const entityElement=e.target.closest(".entities"),stepElement=e.target.closest(".stepcontent");if(!entityElement||!stepElement)return;const pendingPromise=new _pending.default("tool_behat/steps:change"),entityData=await(entityType=e.target.value,_ajax.default.call([{methodname:"tool_behat_get_entity_generator",args:{entitytype:entityType}}])[0]);var entityType;const{html:html,js:js}=await(entityData=>{var _entityData$required;return null!==(_entityData$required=entityData.required)&&void 0!==_entityData$required&&_entityData$required.length?_templates.default.renderForPromise("tool_behat/steprequiredfields",{fields:entityData.required}):Promise.resolve({html:"",js:""})})(entityData),stepRequiredFields=stepElement.querySelector(".steprequiredfields");stepRequiredFields?await _templates.default.replaceNode(stepRequiredFields,html,js):await _templates.default.appendNodeContents(stepElement,html,js),pendingPromise.resolve()}))}})); //# sourceMappingURL=steps.min.js.map build/steps.min.js.map 0000644 00000007673 15152170055 0010713 0 ustar 00 {"version":3,"file":"steps.min.js","sources":["../src/steps.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\nimport Ajax from 'core/ajax';\nimport Templates from 'core/templates';\nimport PendingJS from 'core/pending';\n\n/**\n * Enhancements for the step definitions page.\n *\n * @module tool_behat/steps\n * @copyright 2022 Catalyst IT EU\n * @author Mark Johnson <mark.johnson@catalyst-eu.net>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Call the get_entity_generator web service function\n *\n * Takes the name of an entity generator and returns an object containing a list of the required fields.\n *\n * @param {String} entityType\n * @returns {Promise}\n */\nconst getGeneratorEntities = (entityType) => Ajax.call([{\n methodname: 'tool_behat_get_entity_generator',\n args: {entitytype: entityType}\n}])[0];\n\n/**\n * Render HTML for required fields\n *\n * Takes the entity data returned from getGeneratorEntities and renders the HTML to display the required fields.\n *\n * @param {String} entityData\n * @return {Promise}\n */\nconst getRequiredFieldsContent = (entityData) => {\n if (!entityData.required?.length) {\n return Promise.resolve({\n html: '',\n js: ''\n });\n }\n return Templates.renderForPromise('tool_behat/steprequiredfields', {fields: entityData.required});\n};\n\nexport const init = () => {\n // When an entity is selected in the \"the following exist\" step, fetch and display the required fields.\n document.addEventListener('change', async(e) => {\n const entityElement = e.target.closest('.entities');\n const stepElement = e.target.closest('.stepcontent');\n if (!entityElement || !stepElement) {\n return;\n }\n\n const pendingPromise = new PendingJS('tool_behat/steps:change');\n\n const entityData = await getGeneratorEntities(e.target.value);\n const {html, js} = await getRequiredFieldsContent(entityData);\n\n const stepRequiredFields = stepElement.querySelector('.steprequiredfields');\n if (stepRequiredFields) {\n await Templates.replaceNode(stepRequiredFields, html, js);\n } else {\n await Templates.appendNodeContents(stepElement, html, js);\n }\n pendingPromise.resolve();\n });\n};\n"],"names":["document","addEventListener","async","entityElement","e","target","closest","stepElement","pendingPromise","PendingJS","entityData","entityType","value","Ajax","call","methodname","args","entitytype","html","js","required","_entityData$required","length","Templates","renderForPromise","fields","Promise","resolve","getRequiredFieldsContent","stepRequiredFields","querySelector","replaceNode","appendNodeContents"],"mappings":";;;;;;;;4NA2DoB,KAEhBA,SAASC,iBAAiB,UAAUC,MAAAA,UAC1BC,cAAgBC,EAAEC,OAAOC,QAAQ,aACjCC,YAAcH,EAAEC,OAAOC,QAAQ,oBAChCH,gBAAkBI,yBAIjBC,eAAiB,IAAIC,iBAAU,2BAE/BC,iBAlCgBC,WAkCwBP,EAAEC,OAAOO,MAlClBC,cAAKC,KAAK,CAAC,CACpDC,WAAY,kCACZC,KAAM,CAACC,WAAYN,eACnB,IAH0BA,IAAAA,iBAmChBO,KAACA,KAADC,GAAOA,SAtBaT,CAAAA,0EACzBA,WAAWU,0CAAXC,qBAAqBC,OAMnBC,mBAAUC,iBAAiB,gCAAiC,CAACC,OAAQf,WAAWU,WAL5EM,QAAQC,QAAQ,CACnBT,KAAM,GACNC,GAAI,MAkBiBS,CAAyBlB,YAE5CmB,mBAAqBtB,YAAYuB,cAAc,uBACjDD,yBACMN,mBAAUQ,YAAYF,mBAAoBX,KAAMC,UAEhDI,mBAAUS,mBAAmBzB,YAAaW,KAAMC,IAE1DX,eAAemB"} src/steps.js 0000644 00000005453 15152170055 0007037 0 ustar 00 // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. import Ajax from 'core/ajax'; import Templates from 'core/templates'; import PendingJS from 'core/pending'; /** * Enhancements for the step definitions page. * * @module tool_behat/steps * @copyright 2022 Catalyst IT EU * @author Mark Johnson <mark.johnson@catalyst-eu.net> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** * Call the get_entity_generator web service function * * Takes the name of an entity generator and returns an object containing a list of the required fields. * * @param {String} entityType * @returns {Promise} */ const getGeneratorEntities = (entityType) => Ajax.call([{ methodname: 'tool_behat_get_entity_generator', args: {entitytype: entityType} }])[0]; /** * Render HTML for required fields * * Takes the entity data returned from getGeneratorEntities and renders the HTML to display the required fields. * * @param {String} entityData * @return {Promise} */ const getRequiredFieldsContent = (entityData) => { if (!entityData.required?.length) { return Promise.resolve({ html: '', js: '' }); } return Templates.renderForPromise('tool_behat/steprequiredfields', {fields: entityData.required}); }; export const init = () => { // When an entity is selected in the "the following exist" step, fetch and display the required fields. document.addEventListener('change', async(e) => { const entityElement = e.target.closest('.entities'); const stepElement = e.target.closest('.stepcontent'); if (!entityElement || !stepElement) { return; } const pendingPromise = new PendingJS('tool_behat/steps:change'); const entityData = await getGeneratorEntities(e.target.value); const {html, js} = await getRequiredFieldsContent(entityData); const stepRequiredFields = stepElement.querySelector('.steprequiredfields'); if (stepRequiredFields) { await Templates.replaceNode(stepRequiredFields, html, js); } else { await Templates.appendNodeContents(stepElement, html, js); } pendingPromise.resolve(); }); }; build/repository.min.js.map 0000644 00000005024 15152263317 0011764 0 ustar 00 {"version":3,"file":"repository.min.js","sources":["../src/repository.js"],"sourcesContent":["/**\n * Step management code.\n *\n * @module tool_usertours/managesteps\n * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>\n */\nimport {call as fetchMany} from 'core/ajax';\nimport moodleConfig from 'core/config';\n\n/**\n * Reset the tour state of the specified tour.\n *\n * @param {number} tourid\n * @return {Promise}\n */\nexport const resetTourState = tourid => fetchMany([{\n methodname: 'tool_usertours_reset_tour',\n args: {\n tourid,\n context: moodleConfig.contextid,\n pageurl: window.location.href,\n }\n}])[0];\n\n/**\n * Mark the specified tour as complete.\n *\n * @param {number} stepid\n * @param {number} tourid\n * @param {number} stepindex\n * @return {Promise}\n */\nexport const markTourComplete = (stepid, tourid, stepindex) => fetchMany([{\n methodname: 'tool_usertours_complete_tour',\n args: {\n stepid,\n stepindex: stepindex,\n tourid,\n context: moodleConfig.contextid,\n pageurl: window.location.href,\n }\n}])[0];\n\n/**\n * Fetch the specified tour.\n *\n * @param {number} tourid\n * @return {Promise}\n */\nexport const fetchTour = tourid => fetchMany([{\n methodname: 'tool_usertours_fetch_and_start_tour',\n args: {\n tourid,\n context: moodleConfig.contextid,\n pageurl: window.location.href,\n }\n}])[0];\n\n/**\n * Mark the specified step as having been shown.\n *\n * @param {number} stepid\n * @param {number} tourid\n * @param {number} stepindex\n * @return {Promise}\n */\nexport const markStepShown = (stepid, tourid, stepindex) => fetchMany([{\n methodname: 'tool_usertours_step_shown',\n args: {\n tourid,\n stepid,\n stepindex,\n context: moodleConfig.contextid,\n pageurl: window.location.href,\n }\n}])[0];\n"],"names":["tourid","methodname","args","context","moodleConfig","contextid","pageurl","window","location","href","stepid","stepindex"],"mappings":"8VAe8BA,SAAU,cAAU,CAAC,CAC/CC,WAAY,4BACZC,KAAM,CACFF,OAAAA,OACAG,QAASC,gBAAaC,UACtBC,QAASC,OAAOC,SAASC,SAE7B,6BAU4B,CAACC,OAAQV,OAAQW,aAAc,cAAU,CAAC,CACtEV,WAAY,+BACZC,KAAM,CACFQ,OAAAA,OACAC,UAAWA,UACXX,OAAAA,OACAG,QAASC,gBAAaC,UACtBC,QAASC,OAAOC,SAASC,SAE7B,sBAQqBT,SAAU,cAAU,CAAC,CAC1CC,WAAY,sCACZC,KAAM,CACFF,OAAAA,OACAG,QAASC,gBAAaC,UACtBC,QAASC,OAAOC,SAASC,SAE7B,0BAUyB,CAACC,OAAQV,OAAQW,aAAc,cAAU,CAAC,CACnEV,WAAY,4BACZC,KAAM,CACFF,OAAAA,OACAU,OAAAA,OACAC,UAAAA,UACAR,QAASC,gBAAaC,UACtBC,QAASC,OAAOC,SAASC,SAE7B"} build/managetours.min.js.map 0000644 00000003745 15152263317 0012102 0 ustar 00 {"version":3,"file":"managetours.min.js","sources":["../src/managetours.js"],"sourcesContent":["/**\n * Tour management code.\n *\n * @module tool_usertours/managetours\n * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>\n */\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {confirm as confirmModal} from 'core/notification';\n\n/**\n * Handle tour management actions.\n *\n * @param {Event} e\n * @private\n */\nconst removeTourHandler = e => {\n const deleteButton = e.target.closest('[data-action=\"delete\"]');\n if (deleteButton) {\n e.preventDefault();\n removeTourFromLink(deleteButton.href);\n }\n};\n\n/**\n * Handle removal of a tour with confirmation.\n *\n * @param {string} targetUrl\n * @private\n */\nconst removeTourFromLink = targetUrl => {\n confirmModal(\n getString('confirmtourremovaltitle', 'tool_usertours'),\n getString('confirmtourremovalquestion', 'tool_usertours'),\n getString('yes', 'core'),\n getString('no', 'core'),\n () => {\n window.location = targetUrl;\n }\n );\n};\n\n/**\n * Set up the tour management handlers.\n */\nexport const setup = () => {\n prefetchStrings('tool_usertours', [\n 'confirmtourremovaltitle',\n 'confirmtourremovalquestion',\n ]);\n\n prefetchStrings('core', [\n 'yes',\n 'no',\n ]);\n\n document.querySelector('body').addEventListener('click', removeTourHandler);\n};\n"],"names":["removeTourHandler","e","deleteButton","target","closest","preventDefault","removeTourFromLink","href","targetUrl","window","location","document","querySelector","addEventListener"],"mappings":"oOAgBMA,kBAAoBC,UAChBC,aAAeD,EAAEE,OAAOC,QAAQ,0BAClCF,eACAD,EAAEI,iBACFC,mBAAmBJ,aAAaK,QAUlCD,mBAAqBE,uCAEnB,mBAAU,0BAA2B,mBACrC,mBAAU,6BAA8B,mBACxC,mBAAU,MAAO,SACjB,mBAAU,KAAM,SAChB,KACIC,OAAOC,SAAWF,6BAQT,mCACD,iBAAkB,CAC9B,0BACA,6DAGY,OAAQ,CACpB,MACA,OAGJG,SAASC,cAAc,QAAQC,iBAAiB,QAASb"} build/filter_cssselector.min.js.map 0000644 00000003201 15152263317 0013436 0 ustar 00 {"version":3,"file":"filter_cssselector.min.js","sources":["../src/filter_cssselector.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * CSS selector client side filter.\n *\n * @module tool_usertours/filter_cssselector\n * @copyright 2020 The Open University\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Checks whether the configured CSS selector exists on this page.\n *\n * @param {array} tourConfig The tour configuration.\n * @returns {boolean}\n */\nexport const filterMatches = function(tourConfig) {\n let filterValues = tourConfig.filtervalues.cssselector;\n if (filterValues[0]) {\n return !!document.querySelector(filterValues[0]);\n }\n // If there is no CSS selector configured, this page matches.\n return true;\n};\n"],"names":["tourConfig","filterValues","filtervalues","cssselector","document","querySelector"],"mappings":"wLA6B6B,SAASA,gBAC9BC,aAAeD,WAAWE,aAAaC,mBACvCF,aAAa,MACJG,SAASC,cAAcJ,aAAa"} build/tour.min.js.map 0000644 00000247702 15152263317 0010551 0 ustar 00 {"version":3,"file":"tour.min.js","sources":["../src/tour.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * A user tour.\n *\n * @module tool_usertours/tour\n * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * A list of steps.\n *\n * @typedef {Object[]} StepList\n * @property {Number} stepId The id of the step in the database\n * @property {Number} position The position of the step within the tour (zero-indexed)\n */\n\nimport $ from 'jquery';\nimport * as Aria from 'core/aria';\nimport Popper from 'core/popper';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport {eventTypes} from './events';\nimport {get_string as getString} from 'core/str';\nimport {prefetchStrings} from 'core/prefetch';\n\n/**\n * The minimum spacing for tour step to display.\n *\n * @private\n * @constant\n * @type {number}\n */\nconst MINSPACING = 10;\n\n/**\n * A user tour.\n *\n * @class tool_usertours/tour\n * @property {boolean} tourRunning Whether the tour is currently running.\n */\nconst Tour = class {\n tourRunning = false;\n\n /**\n * @param {object} config The configuration object.\n */\n constructor(config) {\n this.init(config);\n }\n\n /**\n * Initialise the tour.\n *\n * @method init\n * @param {Object} config The configuration object.\n * @chainable\n * @return {Object} this.\n */\n init(config) {\n // Unset all handlers.\n this.eventHandlers = {};\n\n // Reset the current tour states.\n this.reset();\n\n // Store the initial configuration.\n this.originalConfiguration = config || {};\n\n // Apply configuration.\n this.configure.apply(this, arguments);\n\n // Unset recalculate state.\n this.possitionNeedToBeRecalculated = false;\n\n // Unset recalculate count.\n this.recalculatedNo = 0;\n\n try {\n this.storage = window.sessionStorage;\n this.storageKey = 'tourstate_' + this.tourName;\n } catch (e) {\n this.storage = false;\n this.storageKey = '';\n }\n\n prefetchStrings('tool_usertours', [\n 'nextstep_sequence',\n 'skip_tour'\n ]);\n\n return this;\n }\n\n /**\n * Reset the current tour state.\n *\n * @method reset\n * @chainable\n * @return {Object} this.\n */\n reset() {\n // Hide the current step.\n this.hide();\n\n // Unset all handlers.\n this.eventHandlers = [];\n\n // Unset all listeners.\n this.resetStepListeners();\n\n // Unset the original configuration.\n this.originalConfiguration = {};\n\n // Reset the current step number and list of steps.\n this.steps = [];\n\n // Reset the current step number.\n this.currentStepNumber = 0;\n\n return this;\n }\n\n /**\n * Prepare tour configuration.\n *\n * @method configure\n * @param {Object} config The configuration object.\n * @chainable\n * @return {Object} this.\n */\n configure(config) {\n if (typeof config === 'object') {\n // Tour name.\n if (typeof config.tourName !== 'undefined') {\n this.tourName = config.tourName;\n }\n\n // Set up eventHandlers.\n if (config.eventHandlers) {\n for (let eventName in config.eventHandlers) {\n config.eventHandlers[eventName].forEach(function(handler) {\n this.addEventHandler(eventName, handler);\n }, this);\n }\n }\n\n // Reset the step configuration.\n this.resetStepDefaults(true);\n\n // Configure the steps.\n if (typeof config.steps === 'object') {\n this.steps = config.steps;\n }\n\n if (typeof config.template !== 'undefined') {\n this.templateContent = config.template;\n }\n }\n\n // Check that we have enough to start the tour.\n this.checkMinimumRequirements();\n\n return this;\n }\n\n /**\n * Check that the configuration meets the minimum requirements.\n *\n * @method checkMinimumRequirements\n */\n checkMinimumRequirements() {\n // Need a tourName.\n if (!this.tourName) {\n throw new Error(\"Tour Name required\");\n }\n\n // Need a minimum of one step.\n if (!this.steps || !this.steps.length) {\n throw new Error(\"Steps must be specified\");\n }\n }\n\n /**\n * Reset step default configuration.\n *\n * @method resetStepDefaults\n * @param {Boolean} loadOriginalConfiguration Whether to load the original configuration supplied with the Tour.\n * @chainable\n * @return {Object} this.\n */\n resetStepDefaults(loadOriginalConfiguration) {\n if (typeof loadOriginalConfiguration === 'undefined') {\n loadOriginalConfiguration = true;\n }\n\n this.stepDefaults = {};\n if (!loadOriginalConfiguration || typeof this.originalConfiguration.stepDefaults === 'undefined') {\n this.setStepDefaults({});\n } else {\n this.setStepDefaults(this.originalConfiguration.stepDefaults);\n }\n\n return this;\n }\n\n /**\n * Set the step defaults.\n *\n * @method setStepDefaults\n * @param {Object} stepDefaults The step defaults to apply to all steps\n * @chainable\n * @return {Object} this.\n */\n setStepDefaults(stepDefaults) {\n if (!this.stepDefaults) {\n this.stepDefaults = {};\n }\n $.extend(\n this.stepDefaults,\n {\n element: '',\n placement: 'top',\n delay: 0,\n moveOnClick: false,\n moveAfterTime: 0,\n orphan: false,\n direction: 1,\n },\n stepDefaults\n );\n\n return this;\n }\n\n /**\n * Retrieve the current step number.\n *\n * @method getCurrentStepNumber\n * @return {Number} The current step number\n */\n getCurrentStepNumber() {\n return parseInt(this.currentStepNumber, 10);\n }\n\n /**\n * Store the current step number.\n *\n * @method setCurrentStepNumber\n * @param {Number} stepNumber The current step number\n * @chainable\n */\n setCurrentStepNumber(stepNumber) {\n this.currentStepNumber = stepNumber;\n if (this.storage) {\n try {\n this.storage.setItem(this.storageKey, stepNumber);\n } catch (e) {\n if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {\n this.storage.removeItem(this.storageKey);\n }\n }\n }\n }\n\n /**\n * Get the next step number after the currently displayed step.\n *\n * @method getNextStepNumber\n * @param {Number} stepNumber The current step number\n * @return {Number} The next step number to display\n */\n getNextStepNumber(stepNumber) {\n if (typeof stepNumber === 'undefined') {\n stepNumber = this.getCurrentStepNumber();\n }\n let nextStepNumber = stepNumber + 1;\n\n // Keep checking the remaining steps.\n while (nextStepNumber <= this.steps.length) {\n if (this.isStepPotentiallyVisible(this.getStepConfig(nextStepNumber))) {\n return nextStepNumber;\n }\n nextStepNumber++;\n }\n\n return null;\n }\n\n /**\n * Get the previous step number before the currently displayed step.\n *\n * @method getPreviousStepNumber\n * @param {Number} stepNumber The current step number\n * @return {Number} The previous step number to display\n */\n getPreviousStepNumber(stepNumber) {\n if (typeof stepNumber === 'undefined') {\n stepNumber = this.getCurrentStepNumber();\n }\n let previousStepNumber = stepNumber - 1;\n\n // Keep checking the remaining steps.\n while (previousStepNumber >= 0) {\n if (this.isStepPotentiallyVisible(this.getStepConfig(previousStepNumber))) {\n return previousStepNumber;\n }\n previousStepNumber--;\n }\n\n return null;\n }\n\n /**\n * Is the step the final step number?\n *\n * @method isLastStep\n * @param {Number} stepNumber Step number to test\n * @return {Boolean} Whether the step is the final step\n */\n isLastStep(stepNumber) {\n let nextStepNumber = this.getNextStepNumber(stepNumber);\n\n return nextStepNumber === null;\n }\n\n /**\n * Is this step potentially visible?\n *\n * @method isStepPotentiallyVisible\n * @param {Object} stepConfig The step configuration to normalise\n * @return {Boolean} Whether the step is the potentially visible\n */\n isStepPotentiallyVisible(stepConfig) {\n if (!stepConfig) {\n // Without step config, there can be no step.\n return false;\n }\n\n if (this.isStepActuallyVisible(stepConfig)) {\n // If it is actually visible, it is already potentially visible.\n return true;\n }\n\n if (typeof stepConfig.orphan !== 'undefined' && stepConfig.orphan) {\n // Orphan steps have no target. They are always visible.\n return true;\n }\n\n if (typeof stepConfig.delay !== 'undefined' && stepConfig.delay) {\n // Only return true if the activated has not been used yet.\n return true;\n }\n\n // Not theoretically, or actually visible.\n return false;\n }\n\n /**\n * Get potentially visible steps in a tour.\n *\n * @returns {StepList} A list of ordered steps\n */\n getPotentiallyVisibleSteps() {\n let position = 1;\n let result = [];\n // Checking the total steps.\n for (let stepNumber = 0; stepNumber < this.steps.length; stepNumber++) {\n const stepConfig = this.getStepConfig(stepNumber);\n if (this.isStepPotentiallyVisible(stepConfig)) {\n result[stepNumber] = {stepId: stepConfig.stepid, position: position};\n position++;\n }\n }\n\n return result;\n }\n\n /**\n * Is this step actually visible?\n *\n * @method isStepActuallyVisible\n * @param {Object} stepConfig The step configuration to normalise\n * @return {Boolean} Whether the step is actually visible\n */\n isStepActuallyVisible(stepConfig) {\n if (!stepConfig) {\n // Without step config, there can be no step.\n return false;\n }\n\n // Check if the CSS styles are allowed on the browser or not.\n if (!this.isCSSAllowed()) {\n return false;\n }\n\n let target = this.getStepTarget(stepConfig);\n if (target && target.length && target.is(':visible')) {\n // Without a target, there can be no step.\n return !!target.length;\n }\n\n return false;\n }\n\n /**\n * Is the browser actually allow CSS styles?\n *\n * @returns {boolean} True if the browser is allowing CSS styles\n */\n isCSSAllowed() {\n const testCSSElement = document.createElement('div');\n testCSSElement.classList.add('hide');\n document.body.appendChild(testCSSElement);\n const styles = window.getComputedStyle(testCSSElement);\n const isAllowed = styles.display === 'none';\n testCSSElement.remove();\n\n return isAllowed;\n }\n\n /**\n * Go to the next step in the tour.\n *\n * @method next\n * @chainable\n * @return {Object} this.\n */\n next() {\n return this.gotoStep(this.getNextStepNumber());\n }\n\n /**\n * Go to the previous step in the tour.\n *\n * @method previous\n * @chainable\n * @return {Object} this.\n */\n previous() {\n return this.gotoStep(this.getPreviousStepNumber(), -1);\n }\n\n /**\n * Go to the specified step in the tour.\n *\n * @method gotoStep\n * @param {Number} stepNumber The step number to display\n * @param {Number} direction Next or previous step\n * @chainable\n * @return {Object} this.\n * @fires tool_usertours/stepRender\n * @fires tool_usertours/stepRendered\n * @fires tool_usertours/stepHide\n * @fires tool_usertours/stepHidden\n */\n gotoStep(stepNumber, direction) {\n if (stepNumber < 0) {\n return this.endTour();\n }\n\n let stepConfig = this.getStepConfig(stepNumber);\n if (stepConfig === null) {\n return this.endTour();\n }\n\n return this._gotoStep(stepConfig, direction);\n }\n\n _gotoStep(stepConfig, direction) {\n if (!stepConfig) {\n return this.endTour();\n }\n\n if (typeof stepConfig.delay !== 'undefined' && stepConfig.delay && !stepConfig.delayed) {\n stepConfig.delayed = true;\n window.setTimeout(this._gotoStep.bind(this), stepConfig.delay, stepConfig, direction);\n\n return this;\n } else if (!stepConfig.orphan && !this.isStepActuallyVisible(stepConfig)) {\n let fn = direction == -1 ? 'getPreviousStepNumber' : 'getNextStepNumber';\n return this.gotoStep(this[fn](stepConfig.stepNumber), direction);\n }\n\n this.hide();\n\n const stepRenderEvent = this.dispatchEvent(eventTypes.stepRender, {stepConfig}, true);\n if (!stepRenderEvent.defaultPrevented) {\n this.renderStep(stepConfig);\n this.dispatchEvent(eventTypes.stepRendered, {stepConfig});\n }\n\n return this;\n }\n\n /**\n * Fetch the normalised step configuration for the specified step number.\n *\n * @method getStepConfig\n * @param {Number} stepNumber The step number to fetch configuration for\n * @return {Object} The step configuration\n */\n getStepConfig(stepNumber) {\n if (stepNumber === null || stepNumber < 0 || stepNumber >= this.steps.length) {\n return null;\n }\n\n // Normalise the step configuration.\n let stepConfig = this.normalizeStepConfig(this.steps[stepNumber]);\n\n // Add the stepNumber to the stepConfig.\n stepConfig = $.extend(stepConfig, {stepNumber: stepNumber});\n\n return stepConfig;\n }\n\n /**\n * Normalise the supplied step configuration.\n *\n * @method normalizeStepConfig\n * @param {Object} stepConfig The step configuration to normalise\n * @return {Object} The normalised step configuration\n */\n normalizeStepConfig(stepConfig) {\n\n if (typeof stepConfig.reflex !== 'undefined' && typeof stepConfig.moveAfterClick === 'undefined') {\n stepConfig.moveAfterClick = stepConfig.reflex;\n }\n\n if (typeof stepConfig.element !== 'undefined' && typeof stepConfig.target === 'undefined') {\n stepConfig.target = stepConfig.element;\n }\n\n if (typeof stepConfig.content !== 'undefined' && typeof stepConfig.body === 'undefined') {\n stepConfig.body = stepConfig.content;\n }\n\n stepConfig = $.extend({}, this.stepDefaults, stepConfig);\n\n stepConfig = $.extend({}, {\n attachTo: stepConfig.target,\n attachPoint: 'after',\n }, stepConfig);\n\n if (stepConfig.attachTo) {\n stepConfig.attachTo = $(stepConfig.attachTo).first();\n }\n\n return stepConfig;\n }\n\n /**\n * Fetch the actual step target from the selector.\n *\n * This should not be called until after any delay has completed.\n *\n * @method getStepTarget\n * @param {Object} stepConfig The step configuration\n * @return {$}\n */\n getStepTarget(stepConfig) {\n if (stepConfig.target) {\n return $(stepConfig.target);\n }\n\n return null;\n }\n\n /**\n * Fire any event handlers for the specified event.\n *\n * @param {String} eventName The name of the event\n * @param {Object} [detail={}] Any additional details to pass into the eveent\n * @param {Boolean} [cancelable=false] Whether preventDefault() can be called\n * @returns {CustomEvent}\n */\n dispatchEvent(\n eventName,\n detail = {},\n cancelable = false\n ) {\n return dispatchEvent(eventName, {\n // Add the tour to the detail.\n tour: this,\n ...detail,\n }, document, {\n cancelable,\n });\n }\n\n /**\n * @method addEventHandler\n * @param {string} eventName The name of the event to listen for\n * @param {function} handler The event handler to call\n * @return {Object} this.\n */\n addEventHandler(eventName, handler) {\n if (typeof this.eventHandlers[eventName] === 'undefined') {\n this.eventHandlers[eventName] = [];\n }\n\n this.eventHandlers[eventName].push(handler);\n\n return this;\n }\n\n /**\n * Process listeners for the step being shown.\n *\n * @method processStepListeners\n * @param {object} stepConfig The configuration for the step\n * @chainable\n * @return {Object} this.\n */\n processStepListeners(stepConfig) {\n this.listeners.push(\n // Next button.\n {\n node: this.currentStepNode,\n args: ['click', '[data-role=\"next\"]', $.proxy(this.next, this)]\n },\n\n // Close and end tour buttons.\n {\n node: this.currentStepNode,\n args: ['click', '[data-role=\"end\"]', $.proxy(this.endTour, this)]\n },\n\n // Click backdrop and hide tour.\n {\n node: $('[data-flexitour=\"backdrop\"]'),\n args: ['click', $.proxy(this.hide, this)]\n },\n\n // Keypresses.\n {\n node: $('body'),\n args: ['keydown', $.proxy(this.handleKeyDown, this)]\n });\n\n if (stepConfig.moveOnClick) {\n var targetNode = this.getStepTarget(stepConfig);\n this.listeners.push({\n node: targetNode,\n args: ['click', $.proxy(function(e) {\n if ($(e.target).parents('[data-flexitour=\"container\"]').length === 0) {\n // Ignore clicks when they are in the flexitour.\n window.setTimeout($.proxy(this.next, this), 500);\n }\n }, this)]\n });\n }\n\n this.listeners.forEach(function(listener) {\n listener.node.on.apply(listener.node, listener.args);\n });\n\n return this;\n }\n\n /**\n * Reset step listeners.\n *\n * @method resetStepListeners\n * @chainable\n * @return {Object} this.\n */\n resetStepListeners() {\n // Stop listening to all external handlers.\n if (this.listeners) {\n this.listeners.forEach(function(listener) {\n listener.node.off.apply(listener.node, listener.args);\n });\n }\n this.listeners = [];\n\n return this;\n }\n\n /**\n * The standard step renderer.\n *\n * @method renderStep\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n renderStep(stepConfig) {\n // Store the current step configuration for later.\n this.currentStepConfig = stepConfig;\n this.setCurrentStepNumber(stepConfig.stepNumber);\n\n // Fetch the template and convert it to a $ object.\n let template = $(this.getTemplateContent());\n\n // Title.\n template.find('[data-placeholder=\"title\"]')\n .html(stepConfig.title);\n\n // Body.\n template.find('[data-placeholder=\"body\"]')\n .html(stepConfig.body);\n\n // Buttons.\n const nextBtn = template.find('[data-role=\"next\"]');\n const endBtn = template.find('[data-role=\"end\"]');\n\n // Is this the final step?\n if (this.isLastStep(stepConfig.stepNumber)) {\n nextBtn.hide();\n endBtn.removeClass(\"btn-secondary\").addClass(\"btn-primary\");\n } else {\n nextBtn.prop('disabled', false);\n // Use Skip tour label for the End tour button.\n getString('skip_tour', 'tool_usertours').then(value => {\n endBtn.html(value);\n return;\n }).catch();\n }\n\n nextBtn.attr('role', 'button');\n endBtn.attr('role', 'button');\n\n if (this.originalConfiguration.displaystepnumbers) {\n const stepsPotentiallyVisible = this.getPotentiallyVisibleSteps();\n const totalStepsPotentiallyVisible = stepsPotentiallyVisible.length;\n const position = stepsPotentiallyVisible[stepConfig.stepNumber].position;\n if (totalStepsPotentiallyVisible > 1) {\n // Change the label of the Next button to include the sequence.\n getString('nextstep_sequence', 'tool_usertours',\n {position: position, total: totalStepsPotentiallyVisible}).then(value => {\n nextBtn.html(value);\n return;\n }).catch();\n }\n }\n\n // Replace the template with the updated version.\n stepConfig.template = template;\n\n // Add to the page.\n this.addStepToPage(stepConfig);\n\n // Process step listeners after adding to the page.\n // This uses the currentNode.\n this.processStepListeners(stepConfig);\n\n return this;\n }\n\n /**\n * Getter for the template content.\n *\n * @method getTemplateContent\n * @return {$}\n */\n getTemplateContent() {\n return $(this.templateContent).clone();\n }\n\n /**\n * Helper to add a step to the page.\n *\n * @method addStepToPage\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n addStepToPage(stepConfig) {\n // Create the stepNode from the template data.\n let currentStepNode = $('<span data-flexitour=\"container\"></span>')\n .html(stepConfig.template)\n .hide();\n\n // The scroll animation occurs on the body or html.\n let animationTarget = $('body, html')\n .stop(true, true);\n\n if (this.isStepActuallyVisible(stepConfig)) {\n let targetNode = this.getStepTarget(stepConfig);\n\n if (targetNode.parents('[data-usertour=\"scroller\"]').length) {\n animationTarget = targetNode.parents('[data-usertour=\"scroller\"]');\n }\n\n targetNode.data('flexitour', 'target');\n\n let zIndex = this.calculateZIndex(targetNode);\n if (zIndex) {\n stepConfig.zIndex = zIndex + 1;\n }\n\n if (stepConfig.zIndex) {\n currentStepNode.css('zIndex', stepConfig.zIndex + 1);\n }\n\n // Add the backdrop.\n this.positionBackdrop(stepConfig);\n\n $(document.body).append(currentStepNode);\n this.currentStepNode = currentStepNode;\n\n // Ensure that the step node is positioned.\n // Some situations mean that the value is not properly calculated without this step.\n this.currentStepNode.css({\n top: 0,\n left: 0,\n });\n\n animationTarget\n .animate({\n scrollTop: this.calculateScrollTop(stepConfig),\n }).promise().then(function() {\n this.positionStep(stepConfig);\n this.revealStep(stepConfig);\n return;\n }.bind(this))\n .catch(function() {\n // Silently fail.\n });\n\n } else if (stepConfig.orphan) {\n stepConfig.isOrphan = true;\n\n // This will be appended to the body instead.\n stepConfig.attachTo = $('body').first();\n stepConfig.attachPoint = 'append';\n\n // Add the backdrop.\n this.positionBackdrop(stepConfig);\n\n // This is an orphaned step.\n currentStepNode.addClass('orphan');\n\n // It lives in the body.\n $(document.body).append(currentStepNode);\n this.currentStepNode = currentStepNode;\n\n this.currentStepNode.css('position', 'fixed');\n\n this.currentStepPopper = new Popper(\n $('body'),\n this.currentStepNode[0], {\n removeOnDestroy: true,\n placement: stepConfig.placement + '-start',\n arrowElement: '[data-role=\"arrow\"]',\n // Empty the modifiers. We've already placed the step and don't want it moved.\n modifiers: {\n hide: {\n enabled: false,\n },\n applyStyle: {\n onLoad: null,\n enabled: false,\n },\n },\n onCreate: () => {\n // First, we need to check if the step's content contains any images.\n const images = this.currentStepNode.find('img');\n if (images.length) {\n // Images found, need to calculate the position when the image is loaded.\n images.on('load', () => {\n this.calculateStepPositionInPage(currentStepNode);\n });\n }\n this.calculateStepPositionInPage(currentStepNode);\n }\n }\n );\n\n this.revealStep(stepConfig);\n }\n\n return this;\n }\n\n /**\n * Make the given step visible.\n *\n * @method revealStep\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n revealStep(stepConfig) {\n // Fade the step in.\n this.currentStepNode.fadeIn('', $.proxy(function() {\n // Announce via ARIA.\n this.announceStep(stepConfig);\n\n // Focus on the current step Node.\n this.currentStepNode.focus();\n window.setTimeout($.proxy(function() {\n // After a brief delay, focus again.\n // There seems to be an issue with Jaws where it only reads the dialogue title initially.\n // This second focus helps it to read the full dialogue.\n if (this.currentStepNode) {\n this.currentStepNode.focus();\n }\n }, this), 100);\n\n }, this));\n\n return this;\n }\n\n /**\n * Helper to announce the step on the page.\n *\n * @method announceStep\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n announceStep(stepConfig) {\n // Setup the step Dialogue as per:\n // * https://www.w3.org/TR/wai-aria-practices/#dialog_nonmodal\n // * https://www.w3.org/TR/wai-aria-practices/#dialog_modal\n\n // Generate an ID for the current step node.\n let stepId = 'tour-step-' + this.tourName + '-' + stepConfig.stepNumber;\n this.currentStepNode.attr('id', stepId);\n\n let bodyRegion = this.currentStepNode.find('[data-placeholder=\"body\"]').first();\n bodyRegion.attr('id', stepId + '-body');\n bodyRegion.attr('role', 'document');\n\n let headerRegion = this.currentStepNode.find('[data-placeholder=\"title\"]').first();\n headerRegion.attr('id', stepId + '-title');\n headerRegion.attr('aria-labelledby', stepId + '-body');\n\n // Generally, a modal dialog has a role of dialog.\n this.currentStepNode.attr('role', 'dialog');\n this.currentStepNode.attr('tabindex', 0);\n this.currentStepNode.attr('aria-labelledby', stepId + '-title');\n this.currentStepNode.attr('aria-describedby', stepId + '-body');\n\n // Configure ARIA attributes on the target.\n let target = this.getStepTarget(stepConfig);\n if (target) {\n target.data('original-tabindex', target.attr('tabindex'));\n if (!target.attr('tabindex')) {\n target.attr('tabindex', 0);\n }\n\n target\n .data('original-describedby', target.attr('aria-describedby'))\n .attr('aria-describedby', stepId + '-body')\n ;\n }\n\n this.accessibilityShow(stepConfig);\n\n return this;\n }\n\n /**\n * Handle key down events.\n *\n * @method handleKeyDown\n * @param {EventFacade} e\n */\n handleKeyDown(e) {\n let tabbableSelector = 'a[href], link[href], [draggable=true], [contenteditable=true], ';\n tabbableSelector += ':input:enabled, [tabindex], button:enabled';\n switch (e.keyCode) {\n case 27:\n this.endTour();\n break;\n\n // 9 == Tab - trap focus for items with a backdrop.\n case 9:\n // Tab must be handled on key up only in this instance.\n (function() {\n if (!this.currentStepConfig.hasBackdrop) {\n // Trapping tab focus is only handled for those steps with a backdrop.\n return;\n }\n\n // Find all tabbable locations.\n let activeElement = $(document.activeElement);\n let stepTarget = this.getStepTarget(this.currentStepConfig);\n let tabbableNodes = $(tabbableSelector);\n let dialogContainer = $('span[data-flexitour=\"container\"]');\n let currentIndex;\n // Filter out element which is not belong to target section or dialogue.\n if (stepTarget) {\n tabbableNodes = tabbableNodes.filter(function(index, element) {\n return stepTarget !== null\n && (stepTarget.has(element).length\n || dialogContainer.has(element).length\n || stepTarget.is(element)\n || dialogContainer.is(element));\n });\n }\n\n // Find index of focusing element.\n tabbableNodes.each(function(index, element) {\n if (activeElement.is(element)) {\n currentIndex = index;\n return false;\n }\n // Keep looping.\n return true;\n });\n\n let nextIndex;\n let nextNode;\n let focusRelevant;\n if (currentIndex != void 0) {\n let direction = 1;\n if (e.shiftKey) {\n direction = -1;\n }\n nextIndex = currentIndex;\n do {\n nextIndex += direction;\n nextNode = $(tabbableNodes[nextIndex]);\n } while (nextNode.length && nextNode.is(':disabled') || nextNode.is(':hidden'));\n if (nextNode.length) {\n // A new f\n focusRelevant = nextNode.closest(stepTarget).length;\n focusRelevant = focusRelevant || nextNode.closest(this.currentStepNode).length;\n } else {\n // Unable to find the target somehow.\n focusRelevant = false;\n }\n }\n\n if (focusRelevant) {\n nextNode.focus();\n } else {\n if (e.shiftKey) {\n // Focus on the last tabbable node in the step.\n this.currentStepNode.find(tabbableSelector).last().focus();\n } else {\n if (this.currentStepConfig.isOrphan) {\n // Focus on the step - there is no target.\n this.currentStepNode.focus();\n } else {\n // Focus on the step target.\n stepTarget.focus();\n }\n }\n }\n e.preventDefault();\n }).call(this);\n break;\n }\n }\n\n /**\n * Start the current tour.\n *\n * @method startTour\n * @param {Number} startAt Which step number to start at. If not specified, starts at the last point.\n * @chainable\n * @return {Object} this.\n * @fires tool_usertours/tourStart\n * @fires tool_usertours/tourStarted\n */\n startTour(startAt) {\n if (this.storage && typeof startAt === 'undefined') {\n let storageStartValue = this.storage.getItem(this.storageKey);\n if (storageStartValue) {\n let storageStartAt = parseInt(storageStartValue, 10);\n if (storageStartAt <= this.steps.length) {\n startAt = storageStartAt;\n }\n }\n }\n\n if (typeof startAt === 'undefined') {\n startAt = this.getCurrentStepNumber();\n }\n\n const tourStartEvent = this.dispatchEvent(eventTypes.tourStart, {startAt}, true);\n if (!tourStartEvent.defaultPrevented) {\n this.gotoStep(startAt);\n this.tourRunning = true;\n this.dispatchEvent(eventTypes.tourStarted, {startAt});\n }\n\n return this;\n }\n\n /**\n * Restart the tour from the beginning, resetting the completionlag.\n *\n * @method restartTour\n * @chainable\n * @return {Object} this.\n */\n restartTour() {\n return this.startTour(0);\n }\n\n /**\n * End the current tour.\n *\n * @method endTour\n * @chainable\n * @return {Object} this.\n * @fires tool_usertours/tourEnd\n * @fires tool_usertours/tourEnded\n */\n endTour() {\n const tourEndEvent = this.dispatchEvent(eventTypes.tourEnd, {}, true);\n if (tourEndEvent.defaultPrevented) {\n return this;\n }\n\n if (this.currentStepConfig) {\n let previousTarget = this.getStepTarget(this.currentStepConfig);\n if (previousTarget) {\n if (!previousTarget.attr('tabindex')) {\n previousTarget.attr('tabindex', '-1');\n }\n previousTarget.focus();\n }\n }\n\n this.hide(true);\n\n this.tourRunning = false;\n this.dispatchEvent(eventTypes.tourEnded);\n\n return this;\n }\n\n /**\n * Hide any currently visible steps.\n *\n * @method hide\n * @param {Bool} transition Animate the visibility change\n * @chainable\n * @return {Object} this.\n * @fires tool_usertours/stepHide\n * @fires tool_usertours/stepHidden\n */\n hide(transition) {\n const stepHideEvent = this.dispatchEvent(eventTypes.stepHide, {}, true);\n if (stepHideEvent.defaultPrevented) {\n return this;\n }\n\n if (this.currentStepNode && this.currentStepNode.length) {\n this.currentStepNode.hide();\n if (this.currentStepPopper) {\n this.currentStepPopper.destroy();\n }\n }\n\n // Restore original target configuration.\n if (this.currentStepConfig) {\n let target = this.getStepTarget(this.currentStepConfig);\n if (target) {\n if (target.data('original-labelledby')) {\n target.attr('aria-labelledby', target.data('original-labelledby'));\n }\n\n if (target.data('original-describedby')) {\n target.attr('aria-describedby', target.data('original-describedby'));\n }\n\n if (target.data('original-tabindex')) {\n target.attr('tabindex', target.data('tabindex'));\n } else {\n // If the target does not have the tabindex attribute at the beginning. We need to remove it.\n // We should wait a little here before removing the attribute to prevent the browser from adding it again.\n window.setTimeout(() => {\n target.removeAttr('tabindex');\n }, 400);\n }\n }\n\n // Clear the step configuration.\n this.currentStepConfig = null;\n }\n\n let fadeTime = 0;\n if (transition) {\n fadeTime = 400;\n }\n\n // Remove the backdrop features.\n $('[data-flexitour=\"step-background\"]').remove();\n $('[data-flexitour=\"step-backdrop\"]').removeAttr('data-flexitour');\n $('[data-flexitour=\"backdrop\"]').fadeOut(fadeTime, function() {\n $(this).remove();\n });\n\n // Remove aria-describedby and tabindex attributes.\n if (this.currentStepNode && this.currentStepNode.length) {\n let stepId = this.currentStepNode.attr('id');\n if (stepId) {\n let currentStepElement = '[aria-describedby=\"' + stepId + '-body\"]';\n $(currentStepElement).removeAttr('tabindex');\n $(currentStepElement).removeAttr('aria-describedby');\n }\n }\n\n // Reset the listeners.\n this.resetStepListeners();\n\n this.accessibilityHide();\n\n this.dispatchEvent(eventTypes.stepHidden);\n\n this.currentStepNode = null;\n this.currentStepPopper = null;\n return this;\n }\n\n /**\n * Show the current steps.\n *\n * @method show\n * @chainable\n * @return {Object} this.\n */\n show() {\n // Show the current step.\n let startAt = this.getCurrentStepNumber();\n\n return this.gotoStep(startAt);\n }\n\n /**\n * Return the current step node.\n *\n * @method getStepContainer\n * @return {jQuery}\n */\n getStepContainer() {\n return $(this.currentStepNode);\n }\n\n /**\n * Calculate scrollTop.\n *\n * @method calculateScrollTop\n * @param {Object} stepConfig The step configuration of the step\n * @return {Number}\n */\n calculateScrollTop(stepConfig) {\n let viewportHeight = $(window).height();\n let targetNode = this.getStepTarget(stepConfig);\n\n let scrollParent = $(window);\n if (targetNode.parents('[data-usertour=\"scroller\"]').length) {\n scrollParent = targetNode.parents('[data-usertour=\"scroller\"]');\n }\n let scrollTop = scrollParent.scrollTop();\n\n if (stepConfig.placement === 'top') {\n // If the placement is top, center scroll at the top of the target.\n scrollTop = targetNode.offset().top - (viewportHeight / 2);\n } else if (stepConfig.placement === 'bottom') {\n // If the placement is bottom, center scroll at the bottom of the target.\n scrollTop = targetNode.offset().top + targetNode.height() + scrollTop - (viewportHeight / 2);\n } else if (targetNode.height() <= (viewportHeight * 0.8)) {\n // If the placement is left/right, and the target fits in the viewport, centre screen on the target\n scrollTop = targetNode.offset().top - ((viewportHeight - targetNode.height()) / 2);\n } else {\n // If the placement is left/right, and the target is bigger than the viewport, set scrollTop to target.top + buffer\n // and change step attachmentTarget to top+.\n scrollTop = targetNode.offset().top - (viewportHeight * 0.2);\n }\n\n // Never scroll over the top.\n scrollTop = Math.max(0, scrollTop);\n\n // Never scroll beyond the bottom.\n scrollTop = Math.min($(document).height() - viewportHeight, scrollTop);\n\n return Math.ceil(scrollTop);\n }\n\n /**\n * Calculate dialogue position for page middle.\n *\n * @param {jQuery} currentStepNode Current step node\n * @method calculateScrollTop\n */\n calculateStepPositionInPage(currentStepNode) {\n let top = MINSPACING;\n const viewportHeight = $(window).height();\n const stepHeight = currentStepNode.height();\n const viewportWidth = $(window).width();\n const stepWidth = currentStepNode.width();\n if (viewportHeight >= (stepHeight + (MINSPACING * 2))) {\n top = Math.ceil((viewportHeight - stepHeight) / 2);\n } else {\n const headerHeight = currentStepNode.find('.modal-header').first().outerHeight() ?? 0;\n const footerHeight = currentStepNode.find('.modal-footer').first().outerHeight() ?? 0;\n const currentStepBody = currentStepNode.find('[data-placeholder=\"body\"]').first();\n const maxHeight = viewportHeight - (MINSPACING * 2) - headerHeight - footerHeight;\n currentStepBody.css({\n 'max-height': maxHeight + 'px',\n 'overflow': 'auto',\n });\n }\n currentStepNode.offset({\n top: top,\n left: Math.ceil((viewportWidth - stepWidth) / 2)\n });\n }\n\n /**\n * Position the step on the page.\n *\n * @method positionStep\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n positionStep(stepConfig) {\n let content = this.currentStepNode;\n let thisT = this;\n if (!content || !content.length) {\n // Unable to find the step node.\n return this;\n }\n\n stepConfig.placement = this.recalculatePlacement(stepConfig);\n let flipBehavior;\n switch (stepConfig.placement) {\n case 'left':\n flipBehavior = ['left', 'right', 'top', 'bottom'];\n break;\n case 'right':\n flipBehavior = ['right', 'left', 'top', 'bottom'];\n break;\n case 'top':\n flipBehavior = ['top', 'bottom', 'right', 'left'];\n break;\n case 'bottom':\n flipBehavior = ['bottom', 'top', 'right', 'left'];\n break;\n default:\n flipBehavior = 'flip';\n break;\n }\n\n let target = this.getStepTarget(stepConfig);\n var config = {\n placement: stepConfig.placement + '-start',\n removeOnDestroy: true,\n modifiers: {\n flip: {\n behaviour: flipBehavior,\n },\n arrow: {\n element: '[data-role=\"arrow\"]',\n },\n },\n onCreate: function(data) {\n recalculateArrowPosition(data);\n recalculateStepPosition(data);\n },\n onUpdate: function(data) {\n recalculateArrowPosition(data);\n if (thisT.possitionNeedToBeRecalculated) {\n thisT.recalculatedNo++;\n thisT.possitionNeedToBeRecalculated = false;\n recalculateStepPosition(data);\n }\n },\n };\n\n let recalculateArrowPosition = function(data) {\n let placement = data.placement.split('-')[0];\n const isVertical = ['left', 'right'].indexOf(placement) !== -1;\n const arrowElement = data.instance.popper.querySelector('[data-role=\"arrow\"]');\n const stepElement = $(data.instance.popper.querySelector('[data-role=\"flexitour-step\"]'));\n if (isVertical) {\n let arrowHeight = parseFloat(window.getComputedStyle(arrowElement).height);\n let arrowOffset = parseFloat(window.getComputedStyle(arrowElement).top);\n let popperHeight = parseFloat(window.getComputedStyle(data.instance.popper).height);\n let popperOffset = parseFloat(window.getComputedStyle(data.instance.popper).top);\n let popperBorderWidth = parseFloat(stepElement.css('borderTopWidth'));\n let popperBorderRadiusWidth = parseFloat(stepElement.css('borderTopLeftRadius')) * 2;\n let arrowPos = arrowOffset + (arrowHeight / 2);\n let maxPos = popperHeight + popperOffset - popperBorderWidth - popperBorderRadiusWidth;\n let minPos = popperOffset + popperBorderWidth + popperBorderRadiusWidth;\n if (arrowPos >= maxPos || arrowPos <= minPos) {\n let newArrowPos = 0;\n if (arrowPos > (popperHeight / 2)) {\n newArrowPos = maxPos - arrowHeight;\n } else {\n newArrowPos = minPos + arrowHeight;\n }\n $(arrowElement).css('top', newArrowPos);\n }\n } else {\n let arrowWidth = parseFloat(window.getComputedStyle(arrowElement).width);\n let arrowOffset = parseFloat(window.getComputedStyle(arrowElement).left);\n let popperWidth = parseFloat(window.getComputedStyle(data.instance.popper).width);\n let popperOffset = parseFloat(window.getComputedStyle(data.instance.popper).left);\n let popperBorderWidth = parseFloat(stepElement.css('borderTopWidth'));\n let popperBorderRadiusWidth = parseFloat(stepElement.css('borderTopLeftRadius')) * 2;\n let arrowPos = arrowOffset + (arrowWidth / 2);\n let maxPos = popperWidth + popperOffset - popperBorderWidth - popperBorderRadiusWidth;\n let minPos = popperOffset + popperBorderWidth + popperBorderRadiusWidth;\n if (arrowPos >= maxPos || arrowPos <= minPos) {\n let newArrowPos = 0;\n if (arrowPos > (popperWidth / 2)) {\n newArrowPos = maxPos - arrowWidth;\n } else {\n newArrowPos = minPos + arrowWidth;\n }\n $(arrowElement).css('left', newArrowPos);\n }\n }\n };\n\n const recalculateStepPosition = function(data) {\n const placement = data.placement.split('-')[0];\n const isVertical = ['left', 'right'].indexOf(placement) !== -1;\n const popperElement = $(data.instance.popper);\n const targetElement = $(data.instance.reference);\n const arrowElement = popperElement.find('[data-role=\"arrow\"]');\n const stepElement = popperElement.find('[data-role=\"flexitour-step\"]');\n const viewportHeight = $(window).height();\n const viewportWidth = $(window).width();\n const arrowHeight = parseFloat(arrowElement.outerHeight(true));\n const popperHeight = parseFloat(popperElement.outerHeight(true));\n const targetHeight = parseFloat(targetElement.outerHeight(true));\n const arrowWidth = parseFloat(arrowElement.outerWidth(true));\n const popperWidth = parseFloat(popperElement.outerWidth(true));\n const targetWidth = parseFloat(targetElement.outerWidth(true));\n let maxHeight;\n\n if (thisT.recalculatedNo > 1) {\n // The current screen is too small, and cannot fit with the original placement.\n // We should set the placement to auto so the PopperJS can calculate the perfect placement.\n thisT.currentStepPopper.options.placement = isVertical ? 'auto-left' : 'auto-bottom';\n }\n if (thisT.recalculatedNo > 2) {\n // Return here to prevent recursive calling.\n return;\n }\n\n if (isVertical) {\n // Find the best place to put the tour: Left of right.\n const leftSpace = targetElement.offset().left > 0 ? targetElement.offset().left : 0;\n const rightSpace = viewportWidth - leftSpace - targetWidth;\n const remainingSpace = leftSpace >= rightSpace ? leftSpace : rightSpace;\n maxHeight = viewportHeight - MINSPACING * 2;\n if (remainingSpace < (popperWidth + arrowWidth)) {\n const maxWidth = remainingSpace - MINSPACING - arrowWidth;\n if (maxWidth > 0) {\n popperElement.css({\n 'max-width': maxWidth + 'px',\n });\n // Not enough space, flag true to make Popper to recalculate the position.\n thisT.possitionNeedToBeRecalculated = true;\n }\n } else if (maxHeight < popperHeight) {\n // Check if the Popper's height can fit the viewport height or not.\n // If not, set the correct max-height value for the Popper element.\n popperElement.css({\n 'max-height': maxHeight + 'px',\n });\n }\n } else {\n // Find the best place to put the tour: Top of bottom.\n const topSpace = targetElement.offset().top > 0 ? targetElement.offset().top : 0;\n const bottomSpace = viewportHeight - topSpace - targetHeight;\n const remainingSpace = topSpace >= bottomSpace ? topSpace : bottomSpace;\n maxHeight = remainingSpace - MINSPACING - arrowHeight;\n if (remainingSpace < (popperHeight + arrowHeight)) {\n // Not enough space, flag true to make Popper to recalculate the position.\n thisT.possitionNeedToBeRecalculated = true;\n }\n }\n\n // Check if the Popper's height can fit the viewport height or not.\n // If not, set the correct max-height value for the body.\n const currentStepBody = stepElement.find('[data-placeholder=\"body\"]').first();\n const headerEle = stepElement.find('.modal-header').first();\n const footerEle = stepElement.find('.modal-footer').first();\n const headerHeight = headerEle.outerHeight(true) ?? 0;\n const footerHeight = footerEle.outerHeight(true) ?? 0;\n maxHeight = maxHeight - headerHeight - footerHeight;\n if (maxHeight > 0) {\n headerEle.removeClass('minimal');\n footerEle.removeClass('minimal');\n currentStepBody.css({\n 'max-height': maxHeight + 'px',\n 'overflow': 'auto',\n });\n } else {\n headerEle.addClass('minimal');\n footerEle.addClass('minimal');\n }\n // Call the Popper update method to update the position.\n thisT.currentStepPopper.update();\n };\n\n let background = $('[data-flexitour=\"step-background\"]');\n if (background.length) {\n target = background;\n }\n this.currentStepPopper = new Popper(target, content[0], config);\n\n return this;\n }\n\n /**\n * For left/right placement, checks that there is room for the step at current window size.\n *\n * If there is not enough room, changes placement to 'top'.\n *\n * @method recalculatePlacement\n * @param {Object} stepConfig The step configuration of the step\n * @return {String} The placement after recalculate\n */\n recalculatePlacement(stepConfig) {\n const buffer = 10;\n const arrowWidth = 16;\n let target = this.getStepTarget(stepConfig);\n let widthContent = this.currentStepNode.width() + arrowWidth;\n let targetOffsetLeft = target.offset().left - buffer;\n let targetOffsetRight = target.offset().left + target.width() + buffer;\n let placement = stepConfig.placement;\n\n if (['left', 'right'].indexOf(placement) !== -1) {\n if ((targetOffsetLeft < (widthContent + buffer)) &&\n ((targetOffsetRight + widthContent + buffer) > document.documentElement.clientWidth)) {\n placement = 'top';\n }\n }\n return placement;\n }\n\n /**\n * Add the backdrop.\n *\n * @method positionBackdrop\n * @param {Object} stepConfig The step configuration of the step\n * @chainable\n * @return {Object} this.\n */\n positionBackdrop(stepConfig) {\n if (stepConfig.backdrop) {\n this.currentStepConfig.hasBackdrop = true;\n let backdrop = $('<div data-flexitour=\"backdrop\"></div>');\n\n if (stepConfig.zIndex) {\n if (stepConfig.attachPoint === 'append') {\n stepConfig.attachTo.append(backdrop);\n } else {\n backdrop.insertAfter(stepConfig.attachTo);\n }\n } else {\n $('body').append(backdrop);\n }\n\n if (this.isStepActuallyVisible(stepConfig)) {\n // The step has a visible target.\n // Punch a hole through the backdrop.\n let background = $('[data-flexitour=\"step-background\"]');\n if (!background.length) {\n background = $('<div data-flexitour=\"step-background\"></div>');\n }\n\n let targetNode = this.getStepTarget(stepConfig);\n\n let buffer = 10;\n\n let colorNode = targetNode;\n if (buffer) {\n colorNode = $('body');\n }\n\n let drawertop = 0;\n if (targetNode.parents('[data-usertour=\"scroller\"]').length) {\n const scrollerElement = targetNode.parents('[data-usertour=\"scroller\"]');\n const navigationBuffer = scrollerElement.offset().top;\n if (scrollerElement.scrollTop() >= navigationBuffer) {\n drawertop = scrollerElement.scrollTop() - navigationBuffer;\n background.css({\n position: 'fixed'\n });\n }\n }\n\n background.css({\n width: targetNode.outerWidth() + buffer + buffer,\n height: targetNode.outerHeight() + buffer + buffer,\n left: targetNode.offset().left - buffer,\n top: targetNode.offset().top + drawertop - buffer,\n backgroundColor: this.calculateInherittedBackgroundColor(colorNode),\n });\n\n if (targetNode.offset().left < buffer) {\n background.css({\n width: targetNode.outerWidth() + targetNode.offset().left + buffer,\n left: targetNode.offset().left,\n });\n }\n\n if ((targetNode.offset().top + drawertop) < buffer) {\n background.css({\n height: targetNode.outerHeight() + targetNode.offset().top + buffer,\n top: targetNode.offset().top,\n });\n }\n\n let targetRadius = targetNode.css('borderRadius');\n if (targetRadius && targetRadius !== $('body').css('borderRadius')) {\n background.css('borderRadius', targetRadius);\n }\n\n let targetPosition = this.calculatePosition(targetNode);\n if (targetPosition === 'absolute') {\n background.css('position', 'fixed');\n }\n\n let fader = background.clone();\n fader.css({\n backgroundColor: backdrop.css('backgroundColor'),\n opacity: backdrop.css('opacity'),\n });\n fader.attr('data-flexitour', 'step-background-fader');\n\n if (targetNode.parents('[data-region=\"fixed-drawer\"]').length) {\n let targetClone = targetNode.clone();\n background.append(targetClone);\n }\n\n if (stepConfig.zIndex) {\n if (stepConfig.attachPoint === 'append') {\n stepConfig.attachTo.append(background);\n } else {\n fader.insertAfter(stepConfig.attachTo);\n background.insertAfter(stepConfig.attachTo);\n }\n } else {\n $('body').append(fader);\n $('body').append(background);\n }\n\n // Add the backdrop data to the actual target.\n // This is the part which actually does the work.\n targetNode.attr('data-flexitour', 'step-backdrop');\n\n if (stepConfig.zIndex) {\n backdrop.css('zIndex', stepConfig.zIndex);\n background.css('zIndex', stepConfig.zIndex + 1);\n targetNode.css('zIndex', stepConfig.zIndex + 2);\n }\n\n fader.fadeOut('2000', function() {\n $(this).remove();\n });\n }\n }\n return this;\n }\n\n /**\n * Calculate the inheritted z-index.\n *\n * @method calculateZIndex\n * @param {jQuery} elem The element to calculate z-index for\n * @return {Number} Calculated z-index\n */\n calculateZIndex(elem) {\n elem = $(elem);\n while (elem.length && elem[0] !== document) {\n // Ignore z-index if position is set to a value where z-index is ignored by the browser\n // This makes behavior of this function consistent across browsers\n // WebKit always returns auto if the element is positioned.\n let position = elem.css(\"position\");\n if (position === \"absolute\" || position === \"relative\" || position === \"fixed\") {\n // IE returns 0 when zIndex is not specified\n // other browsers return a string\n // we ignore the case of nested elements with an explicit value of 0\n // <div style=\"z-index: -10;\"><div style=\"z-index: 0;\"></div></div>\n let value = parseInt(elem.css(\"zIndex\"), 10);\n if (!isNaN(value) && value !== 0) {\n return value;\n }\n }\n elem = elem.parent();\n }\n\n return 0;\n }\n\n /**\n * Calculate the inheritted background colour.\n *\n * @method calculateInherittedBackgroundColor\n * @param {jQuery} elem The element to calculate colour for\n * @return {String} Calculated background colour\n */\n calculateInherittedBackgroundColor(elem) {\n // Use a fake node to compare each element against.\n let fakeNode = $('<div>').hide();\n $('body').append(fakeNode);\n let fakeElemColor = fakeNode.css('backgroundColor');\n fakeNode.remove();\n\n elem = $(elem);\n while (elem.length && elem[0] !== document) {\n let color = elem.css('backgroundColor');\n if (color !== fakeElemColor) {\n return color;\n }\n elem = elem.parent();\n }\n\n return null;\n }\n\n /**\n * Calculate the inheritted position.\n *\n * @method calculatePosition\n * @param {jQuery} elem The element to calculate position for\n * @return {String} Calculated position\n */\n calculatePosition(elem) {\n elem = $(elem);\n while (elem.length && elem[0] !== document) {\n let position = elem.css('position');\n if (position !== 'static') {\n return position;\n }\n elem = elem.parent();\n }\n\n return null;\n }\n\n /**\n * Perform accessibility changes for step shown.\n *\n * This will add aria-hidden=\"true\" to all siblings and parent siblings.\n *\n * @method accessibilityShow\n */\n accessibilityShow() {\n let stateHolder = 'data-has-hidden';\n let attrName = 'aria-hidden';\n let hideFunction = function(child) {\n let flexitourRole = child.data('flexitour');\n if (flexitourRole) {\n switch (flexitourRole) {\n case 'container':\n case 'target':\n return;\n }\n }\n\n let hidden = child.attr(attrName);\n if (!hidden) {\n child.attr(stateHolder, true);\n Aria.hide(child);\n }\n };\n\n this.currentStepNode.siblings().each(function(index, node) {\n hideFunction($(node));\n });\n this.currentStepNode.parentsUntil('body').siblings().each(function(index, node) {\n hideFunction($(node));\n });\n }\n\n /**\n * Perform accessibility changes for step hidden.\n *\n * This will remove any newly added aria-hidden=\"true\".\n *\n * @method accessibilityHide\n */\n accessibilityHide() {\n let stateHolder = 'data-has-hidden';\n let showFunction = function(child) {\n let hidden = child.attr(stateHolder);\n if (typeof hidden !== 'undefined') {\n child.removeAttr(stateHolder);\n Aria.unhide(child);\n }\n };\n\n $('[' + stateHolder + ']').each(function(index, node) {\n showFunction($(node));\n });\n }\n};\n\nexport default Tour;\n"],"names":["constructor","config","init","eventHandlers","reset","originalConfiguration","configure","apply","this","arguments","possitionNeedToBeRecalculated","recalculatedNo","storage","window","sessionStorage","storageKey","tourName","e","hide","resetStepListeners","steps","currentStepNumber","eventName","forEach","handler","addEventHandler","resetStepDefaults","template","templateContent","checkMinimumRequirements","Error","length","loadOriginalConfiguration","stepDefaults","setStepDefaults","extend","element","placement","delay","moveOnClick","moveAfterTime","orphan","direction","getCurrentStepNumber","parseInt","setCurrentStepNumber","stepNumber","setItem","code","DOMException","QUOTA_EXCEEDED_ERR","removeItem","getNextStepNumber","nextStepNumber","isStepPotentiallyVisible","getStepConfig","getPreviousStepNumber","previousStepNumber","isLastStep","stepConfig","isStepActuallyVisible","getPotentiallyVisibleSteps","position","result","stepId","stepid","isCSSAllowed","target","getStepTarget","is","testCSSElement","document","createElement","classList","add","body","appendChild","isAllowed","getComputedStyle","display","remove","next","gotoStep","previous","endTour","_gotoStep","delayed","setTimeout","bind","fn","dispatchEvent","eventTypes","stepRender","defaultPrevented","renderStep","stepRendered","normalizeStepConfig","$","reflex","moveAfterClick","content","attachTo","attachPoint","first","detail","cancelable","tour","push","processStepListeners","listeners","node","currentStepNode","args","proxy","handleKeyDown","targetNode","parents","listener","on","off","currentStepConfig","getTemplateContent","find","html","title","nextBtn","endBtn","removeClass","addClass","prop","then","value","catch","attr","displaystepnumbers","stepsPotentiallyVisible","totalStepsPotentiallyVisible","total","addStepToPage","clone","animationTarget","stop","data","zIndex","calculateZIndex","css","positionBackdrop","append","top","left","animate","scrollTop","calculateScrollTop","promise","positionStep","revealStep","isOrphan","currentStepPopper","Popper","removeOnDestroy","arrowElement","modifiers","enabled","applyStyle","onLoad","onCreate","images","calculateStepPositionInPage","fadeIn","announceStep","focus","bodyRegion","headerRegion","accessibilityShow","tabbableSelector","keyCode","hasBackdrop","currentIndex","nextIndex","nextNode","focusRelevant","activeElement","stepTarget","tabbableNodes","dialogContainer","filter","index","has","each","shiftKey","closest","last","preventDefault","call","startTour","startAt","storageStartValue","getItem","storageStartAt","tourStart","tourRunning","tourStarted","restartTour","tourEnd","previousTarget","tourEnded","transition","stepHide","destroy","removeAttr","fadeTime","fadeOut","currentStepElement","accessibilityHide","stepHidden","show","getStepContainer","viewportHeight","height","scrollParent","offset","Math","max","min","ceil","stepHeight","viewportWidth","width","stepWidth","MINSPACING","maxHeight","outerHeight","flipBehavior","thisT","recalculatePlacement","flip","behaviour","arrow","recalculateArrowPosition","recalculateStepPosition","onUpdate","split","isVertical","indexOf","instance","popper","querySelector","stepElement","arrowHeight","parseFloat","arrowOffset","popperHeight","popperOffset","popperBorderWidth","popperBorderRadiusWidth","arrowPos","maxPos","minPos","newArrowPos","arrowWidth","popperWidth","popperElement","targetElement","reference","targetHeight","outerWidth","targetWidth","options","leftSpace","rightSpace","remainingSpace","maxWidth","topSpace","bottomSpace","currentStepBody","headerEle","footerEle","update","background","widthContent","targetOffsetLeft","targetOffsetRight","documentElement","clientWidth","backdrop","insertAfter","buffer","colorNode","drawertop","scrollerElement","navigationBuffer","backgroundColor","calculateInherittedBackgroundColor","targetRadius","calculatePosition","fader","opacity","targetClone","elem","isNaN","parent","fakeNode","fakeElemColor","color","hideFunction","child","flexitourRole","Aria","siblings","parentsUntil","unhide"],"mappings":"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"} build/usertours.min.js.map 0000644 00000025512 15152263317 0011624 0 ustar 00 {"version":3,"file":"usertours.min.js","sources":["../src/usertours.js"],"sourcesContent":["/**\n * User tour control library.\n *\n * @module tool_usertours/usertours\n * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>\n */\nimport BootstrapTour from './tour';\nimport Templates from 'core/templates';\nimport log from 'core/log';\nimport notification from 'core/notification';\nimport * as tourRepository from './repository';\nimport Pending from 'core/pending';\nimport {eventTypes} from './events';\n\nlet currentTour = null;\nlet tourId = null;\nlet restartTourAndKeepProgress = false;\nlet currentStepNo = null;\n\n/**\n * Find the first matching tour.\n *\n * @param {object[]} tourDetails\n * @param {object[]} filters\n * @returns {null|object}\n */\nconst findMatchingTour = (tourDetails, filters) => {\n return tourDetails.find(tour => filters.some(filter => {\n if (filter && filter.filterMatches) {\n return filter.filterMatches(tour);\n }\n\n return true;\n }));\n};\n\n/**\n * Initialise the user tour for the current page.\n *\n * @method init\n * @param {Array} tourDetails The matching tours for this page.\n * @param {Array} filters The names of all client side filters.\n */\nexport const init = async(tourDetails, filters) => {\n const requirements = [];\n filters.forEach(filter => {\n requirements.push(import(`tool_usertours/filter_${filter}`));\n });\n\n const filterPlugins = await Promise.all(requirements);\n\n const matchingTour = findMatchingTour(tourDetails, filterPlugins);\n if (!matchingTour) {\n return;\n }\n\n // Only one tour per page is allowed.\n tourId = matchingTour.tourId;\n\n let startTour = matchingTour.startTour;\n if (typeof startTour === 'undefined') {\n startTour = true;\n }\n\n if (startTour) {\n // Fetch the tour configuration.\n fetchTour(tourId);\n }\n\n addResetLink();\n\n // Watch for the reset link.\n document.querySelector('body').addEventListener('click', e => {\n const resetLink = e.target.closest('#resetpagetour');\n if (resetLink) {\n e.preventDefault();\n resetTourState(tourId);\n }\n });\n\n // Watch for the resize event.\n window.addEventListener(\"resize\", () => {\n // Only listen for the running tour.\n if (currentTour && currentTour.tourRunning) {\n clearTimeout(window.resizedFinished);\n window.resizedFinished = setTimeout(() => {\n // Wait until the resize event has finished.\n currentStepNo = currentTour.getCurrentStepNumber();\n restartTourAndKeepProgress = true;\n resetTourState(tourId);\n }, 250);\n }\n });\n};\n\n/**\n * Fetch the configuration specified tour, and start the tour when it has been fetched.\n *\n * @method fetchTour\n * @param {Number} tourId The ID of the tour to start.\n */\nconst fetchTour = async tourId => {\n const pendingPromise = new Pending(`admin_usertour_fetchTour:${tourId}`);\n\n try {\n // If we don't have any tour config (because it doesn't need showing for the current user), return early.\n const response = await tourRepository.fetchTour(tourId);\n if (response.hasOwnProperty('tourconfig')) {\n const {html} = await Templates.renderForPromise('tool_usertours/tourstep', response.tourconfig);\n startBootstrapTour(tourId, html, response.tourconfig);\n }\n pendingPromise.resolve();\n } catch (error) {\n pendingPromise.resolve();\n notification.exception(error);\n }\n};\n\nconst getPreferredResetLocation = () => {\n let location = document.querySelector('.tool_usertours-resettourcontainer');\n if (location) {\n return location;\n }\n\n location = document.querySelector('.logininfo');\n if (location) {\n return location;\n }\n\n location = document.querySelector('footer');\n if (location) {\n return location;\n }\n\n return document.body;\n};\n\n/**\n * Add a reset link to the page.\n *\n * @method addResetLink\n */\nconst addResetLink = () => {\n const pendingPromise = new Pending('admin_usertour_addResetLink');\n\n Templates.render('tool_usertours/resettour', {})\n .then(function(html, js) {\n // Append the link to the most suitable place on the page with fallback to legacy selectors and finally the body if\n // there is no better place.\n Templates.appendNodeContents(getPreferredResetLocation(), html, js);\n\n return;\n })\n .catch()\n .then(pendingPromise.resolve)\n .catch();\n};\n\n/**\n * Start the specified tour.\n *\n * @method startBootstrapTour\n * @param {Number} tourId The ID of the tour to start.\n * @param {String} template The template to use.\n * @param {Object} tourConfig The tour configuration.\n * @return {Object}\n */\nconst startBootstrapTour = (tourId, template, tourConfig) => {\n if (currentTour && currentTour.tourRunning) {\n // End the current tour.\n currentTour.endTour();\n currentTour = null;\n }\n\n document.addEventListener(eventTypes.tourEnded, markTourComplete);\n document.addEventListener(eventTypes.stepRenderer, markStepShown);\n\n // Sort out the tour name.\n tourConfig.tourName = tourConfig.name;\n delete tourConfig.name;\n\n // Add the template to the configuration.\n // This enables translations of the buttons.\n tourConfig.template = template;\n\n tourConfig.steps = tourConfig.steps.map(function(step) {\n if (typeof step.element !== 'undefined') {\n step.target = step.element;\n delete step.element;\n }\n\n if (typeof step.reflex !== 'undefined') {\n step.moveOnClick = !!step.reflex;\n delete step.reflex;\n }\n\n if (typeof step.content !== 'undefined') {\n step.body = step.content;\n delete step.content;\n }\n\n return step;\n });\n\n currentTour = new BootstrapTour(tourConfig);\n let startAt = 0;\n if (restartTourAndKeepProgress && currentStepNo) {\n startAt = currentStepNo;\n restartTourAndKeepProgress = false;\n currentStepNo = null;\n }\n return currentTour.startTour(startAt);\n};\n\n/**\n * Mark the specified step as being shownd by the user.\n *\n * @method markStepShown\n * @param {Event} e\n */\nconst markStepShown = e => {\n const tour = e.detail.tour;\n const stepConfig = tour.getStepConfig(tour.getCurrentStepNumber());\n tourRepository.markStepShown(\n stepConfig.stepid,\n tourId,\n tour.getCurrentStepNumber()\n ).catch(log.error);\n};\n\n/**\n * Mark the specified tour as being completed by the user.\n *\n * @method markTourComplete\n * @param {Event} e\n * @listens tool_usertours/stepRendered\n */\nconst markTourComplete = e => {\n document.removeEventListener(eventTypes.tourEnded, markTourComplete);\n document.removeEventListener(eventTypes.stepRenderer, markStepShown);\n\n const tour = e.detail.tour;\n const stepConfig = tour.getStepConfig(tour.getCurrentStepNumber());\n tourRepository.markTourComplete(\n stepConfig.stepid,\n tourId,\n tour.getCurrentStepNumber()\n ).catch(log.error);\n};\n\n/**\n * Reset the state, and restart the the tour on the current page.\n *\n * @method resetTourState\n * @param {Number} tourId The ID of the tour to start.\n * @returns {Promise}\n */\nexport const resetTourState = tourId => tourRepository.resetTourState(tourId)\n.then(response => {\n if (response.startTour) {\n fetchTour(response.startTour);\n }\n return;\n}).catch(notification.exception);\n"],"names":["currentTour","tourId","restartTourAndKeepProgress","currentStepNo","async","tourDetails","filters","requirements","forEach","filter","push","matchingTour","find","tour","some","filterMatches","findMatchingTour","Promise","all","startTour","fetchTour","addResetLink","document","querySelector","addEventListener","e","target","closest","preventDefault","resetTourState","window","tourRunning","clearTimeout","resizedFinished","setTimeout","getCurrentStepNumber","pendingPromise","Pending","response","tourRepository","hasOwnProperty","html","Templates","renderForPromise","tourconfig","startBootstrapTour","resolve","error","exception","render","then","js","appendNodeContents","location","body","getPreferredResetLocation","catch","template","tourConfig","endTour","eventTypes","tourEnded","markTourComplete","stepRenderer","markStepShown","tourName","name","steps","map","step","element","reflex","moveOnClick","content","BootstrapTour","startAt","detail","stepConfig","getStepConfig","stepid","log","removeEventListener","notification"],"mappings":"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"} build/repository.min.js 0000644 00000002260 15152263317 0011207 0 ustar 00 define("tool_usertours/repository",["exports","core/ajax","core/config"],(function(_exports,_ajax,_config){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.resetTourState=_exports.markTourComplete=_exports.markStepShown=_exports.fetchTour=void 0,_config=(obj=_config)&&obj.__esModule?obj:{default:obj};_exports.resetTourState=tourid=>(0,_ajax.call)([{methodname:"tool_usertours_reset_tour",args:{tourid:tourid,context:_config.default.contextid,pageurl:window.location.href}}])[0];_exports.markTourComplete=(stepid,tourid,stepindex)=>(0,_ajax.call)([{methodname:"tool_usertours_complete_tour",args:{stepid:stepid,stepindex:stepindex,tourid:tourid,context:_config.default.contextid,pageurl:window.location.href}}])[0];_exports.fetchTour=tourid=>(0,_ajax.call)([{methodname:"tool_usertours_fetch_and_start_tour",args:{tourid:tourid,context:_config.default.contextid,pageurl:window.location.href}}])[0];_exports.markStepShown=(stepid,tourid,stepindex)=>(0,_ajax.call)([{methodname:"tool_usertours_step_shown",args:{tourid:tourid,stepid:stepid,stepindex:stepindex,context:_config.default.contextid,pageurl:window.location.href}}])[0]})); //# sourceMappingURL=repository.min.js.map build/managesteps.min.js.map 0000644 00000003745 15152263317 0012064 0 ustar 00 {"version":3,"file":"managesteps.min.js","sources":["../src/managesteps.js"],"sourcesContent":["/**\n * Step management code.\n *\n * @module tool_usertours/managesteps\n * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk>\n */\nimport {prefetchStrings} from 'core/prefetch';\nimport {get_string as getString} from 'core/str';\nimport {confirm as confirmModal} from 'core/notification';\n\n/**\n * Handle step management actions.\n *\n * @param {Event} e\n * @private\n */\nconst removeStepHandler = e => {\n const deleteButton = e.target.closest('[data-action=\"delete\"]');\n if (deleteButton) {\n e.preventDefault();\n removeStepFromLink(deleteButton.href);\n }\n};\n\n/**\n * Handle removal of a step with confirmation.\n *\n * @param {string} targetUrl\n * @private\n */\nconst removeStepFromLink = targetUrl => {\n confirmModal(\n getString('confirmstepremovaltitle', 'tool_usertours'),\n getString('confirmstepremovalquestion', 'tool_usertours'),\n getString('yes', 'core'),\n getString('no', 'core'),\n () => {\n window.location = targetUrl;\n }\n );\n};\n\n/**\n * Set up the step management handlers.\n */\nexport const setup = () => {\n prefetchStrings('tool_usertours', [\n 'confirmstepremovaltitle',\n 'confirmstepremovalquestion',\n ]);\n\n prefetchStrings('core', [\n 'yes',\n 'no',\n ]);\n\n document.querySelector('body').addEventListener('click', removeStepHandler);\n};\n"],"names":["removeStepHandler","e","deleteButton","target","closest","preventDefault","removeStepFromLink","href","targetUrl","window","location","document","querySelector","addEventListener"],"mappings":"oOAgBMA,kBAAoBC,UAChBC,aAAeD,EAAEE,OAAOC,QAAQ,0BAClCF,eACAD,EAAEI,iBACFC,mBAAmBJ,aAAaK,QAUlCD,mBAAqBE,uCAEnB,mBAAU,0BAA2B,mBACrC,mBAAU,6BAA8B,mBACxC,mBAAU,MAAO,SACjB,mBAAU,KAAM,SAChB,KACIC,OAAOC,SAAWF,6BAQT,mCACD,iBAAkB,CAC9B,0BACA,6DAGY,OAAQ,CACpB,MACA,OAGJG,SAASC,cAAc,QAAQC,iBAAiB,QAASb"} build/managesteps.min.js 0000644 00000001721 15152263317 0011300 0 ustar 00 define("tool_usertours/managesteps",["exports","core/prefetch","core/str","core/notification"],(function(_exports,_prefetch,_str,_notification){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setup=void 0;const removeStepHandler=e=>{const deleteButton=e.target.closest('[data-action="delete"]');deleteButton&&(e.preventDefault(),removeStepFromLink(deleteButton.href))},removeStepFromLink=targetUrl=>{(0,_notification.confirm)((0,_str.get_string)("confirmstepremovaltitle","tool_usertours"),(0,_str.get_string)("confirmstepremovalquestion","tool_usertours"),(0,_str.get_string)("yes","core"),(0,_str.get_string)("no","core"),(()=>{window.location=targetUrl}))};_exports.setup=()=>{(0,_prefetch.prefetchStrings)("tool_usertours",["confirmstepremovaltitle","confirmstepremovalquestion"]),(0,_prefetch.prefetchStrings)("core",["yes","no"]),document.querySelector("body").addEventListener("click",removeStepHandler)}})); //# sourceMappingURL=managesteps.min.js.map build/usertours.min.js 0000644 00000013631 15152263317 0011047 0 ustar 00 define("tool_usertours/usertours",["exports","./tour","core/templates","core/log","core/notification","./repository","core/pending","./events"],(function(_exports,_tour,_templates,_log,_notification,tourRepository,_pending,_events){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.resetTourState=_exports.init=void 0,_tour=_interopRequireDefault(_tour),_templates=_interopRequireDefault(_templates),_log=_interopRequireDefault(_log),_notification=_interopRequireDefault(_notification),tourRepository=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(tourRepository),_pending=_interopRequireDefault(_pending);var _systemImportTransformerGlobalIdentifier="undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{};function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}let currentTour=null,tourId=null,restartTourAndKeepProgress=!1,currentStepNo=null;_exports.init=async(tourDetails,filters)=>{const requirements=[];filters.forEach((filter=>{requirements.push("function"==typeof _systemImportTransformerGlobalIdentifier.define&&_systemImportTransformerGlobalIdentifier.define.amd?new Promise((function(resolve,reject){_systemImportTransformerGlobalIdentifier.require(["tool_usertours/filter_".concat(filter)],resolve,reject)})):"undefined"!=typeof module&&module.exports&&"undefined"!=typeof require||"undefined"!=typeof module&&module.component&&_systemImportTransformerGlobalIdentifier.require&&"component"===_systemImportTransformerGlobalIdentifier.require.loader?Promise.resolve(require("tool_usertours/filter_".concat(filter))):Promise.resolve(_systemImportTransformerGlobalIdentifier["tool_usertours/filter_".concat(filter)]))}));const matchingTour=((tourDetails,filters)=>tourDetails.find((tour=>filters.some((filter=>!filter||!filter.filterMatches||filter.filterMatches(tour))))))(tourDetails,await Promise.all(requirements));if(!matchingTour)return;tourId=matchingTour.tourId;let startTour=matchingTour.startTour;void 0===startTour&&(startTour=!0),startTour&&fetchTour(tourId),addResetLink(),document.querySelector("body").addEventListener("click",(e=>{e.target.closest("#resetpagetour")&&(e.preventDefault(),resetTourState(tourId))})),window.addEventListener("resize",(()=>{currentTour&¤tTour.tourRunning&&(clearTimeout(window.resizedFinished),window.resizedFinished=setTimeout((()=>{currentStepNo=currentTour.getCurrentStepNumber(),restartTourAndKeepProgress=!0,resetTourState(tourId)}),250))}))};const fetchTour=async tourId=>{const pendingPromise=new _pending.default("admin_usertour_fetchTour:".concat(tourId));try{const response=await tourRepository.fetchTour(tourId);if(response.hasOwnProperty("tourconfig")){const{html:html}=await _templates.default.renderForPromise("tool_usertours/tourstep",response.tourconfig);startBootstrapTour(tourId,html,response.tourconfig)}pendingPromise.resolve()}catch(error){pendingPromise.resolve(),_notification.default.exception(error)}},addResetLink=()=>{const pendingPromise=new _pending.default("admin_usertour_addResetLink");_templates.default.render("tool_usertours/resettour",{}).then((function(html,js){_templates.default.appendNodeContents((()=>{let location=document.querySelector(".tool_usertours-resettourcontainer");return location||(location=document.querySelector(".logininfo"),location||(location=document.querySelector("footer"),location||document.body))})(),html,js)})).catch().then(pendingPromise.resolve).catch()},startBootstrapTour=(tourId,template,tourConfig)=>{currentTour&¤tTour.tourRunning&&(currentTour.endTour(),currentTour=null),document.addEventListener(_events.eventTypes.tourEnded,markTourComplete),document.addEventListener(_events.eventTypes.stepRenderer,markStepShown),tourConfig.tourName=tourConfig.name,delete tourConfig.name,tourConfig.template=template,tourConfig.steps=tourConfig.steps.map((function(step){return void 0!==step.element&&(step.target=step.element,delete step.element),void 0!==step.reflex&&(step.moveOnClick=!!step.reflex,delete step.reflex),void 0!==step.content&&(step.body=step.content,delete step.content),step})),currentTour=new _tour.default(tourConfig);let startAt=0;return restartTourAndKeepProgress&¤tStepNo&&(startAt=currentStepNo,restartTourAndKeepProgress=!1,currentStepNo=null),currentTour.startTour(startAt)},markStepShown=e=>{const tour=e.detail.tour,stepConfig=tour.getStepConfig(tour.getCurrentStepNumber());tourRepository.markStepShown(stepConfig.stepid,tourId,tour.getCurrentStepNumber()).catch(_log.default.error)},markTourComplete=e=>{document.removeEventListener(_events.eventTypes.tourEnded,markTourComplete),document.removeEventListener(_events.eventTypes.stepRenderer,markStepShown);const tour=e.detail.tour,stepConfig=tour.getStepConfig(tour.getCurrentStepNumber());tourRepository.markTourComplete(stepConfig.stepid,tourId,tour.getCurrentStepNumber()).catch(_log.default.error)},resetTourState=tourId=>tourRepository.resetTourState(tourId).then((response=>{response.startTour&&fetchTour(response.startTour)})).catch(_notification.default.exception);_exports.resetTourState=resetTourState})); //# sourceMappingURL=usertours.min.js.map build/tour.min.js 0000644 00000070733 15152263317 0007773 0 ustar 00 define("tool_usertours/tour",["exports","jquery","core/aria","core/popper","core/event_dispatcher","./events","core/str","core/prefetch"],(function(_exports,_jquery,Aria,_popper,_event_dispatcher,_events,_str,_prefetch){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),Aria=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(Aria),_popper=_interopRequireDefault(_popper);var _default=class{constructor(config){var obj,key,value;value=!1,(key="tourRunning")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.init(config)}init(config){this.eventHandlers={},this.reset(),this.originalConfiguration=config||{},this.configure.apply(this,arguments),this.possitionNeedToBeRecalculated=!1,this.recalculatedNo=0;try{this.storage=window.sessionStorage,this.storageKey="tourstate_"+this.tourName}catch(e){this.storage=!1,this.storageKey=""}return(0,_prefetch.prefetchStrings)("tool_usertours",["nextstep_sequence","skip_tour"]),this}reset(){return this.hide(),this.eventHandlers=[],this.resetStepListeners(),this.originalConfiguration={},this.steps=[],this.currentStepNumber=0,this}configure(config){if("object"==typeof config){if(void 0!==config.tourName&&(this.tourName=config.tourName),config.eventHandlers)for(let eventName in config.eventHandlers)config.eventHandlers[eventName].forEach((function(handler){this.addEventHandler(eventName,handler)}),this);this.resetStepDefaults(!0),"object"==typeof config.steps&&(this.steps=config.steps),void 0!==config.template&&(this.templateContent=config.template)}return this.checkMinimumRequirements(),this}checkMinimumRequirements(){if(!this.tourName)throw new Error("Tour Name required");if(!this.steps||!this.steps.length)throw new Error("Steps must be specified")}resetStepDefaults(loadOriginalConfiguration){return void 0===loadOriginalConfiguration&&(loadOriginalConfiguration=!0),this.stepDefaults={},loadOriginalConfiguration&&void 0!==this.originalConfiguration.stepDefaults?this.setStepDefaults(this.originalConfiguration.stepDefaults):this.setStepDefaults({}),this}setStepDefaults(stepDefaults){return this.stepDefaults||(this.stepDefaults={}),_jquery.default.extend(this.stepDefaults,{element:"",placement:"top",delay:0,moveOnClick:!1,moveAfterTime:0,orphan:!1,direction:1},stepDefaults),this}getCurrentStepNumber(){return parseInt(this.currentStepNumber,10)}setCurrentStepNumber(stepNumber){if(this.currentStepNumber=stepNumber,this.storage)try{this.storage.setItem(this.storageKey,stepNumber)}catch(e){e.code===DOMException.QUOTA_EXCEEDED_ERR&&this.storage.removeItem(this.storageKey)}}getNextStepNumber(stepNumber){void 0===stepNumber&&(stepNumber=this.getCurrentStepNumber());let nextStepNumber=stepNumber+1;for(;nextStepNumber<=this.steps.length;){if(this.isStepPotentiallyVisible(this.getStepConfig(nextStepNumber)))return nextStepNumber;nextStepNumber++}return null}getPreviousStepNumber(stepNumber){void 0===stepNumber&&(stepNumber=this.getCurrentStepNumber());let previousStepNumber=stepNumber-1;for(;previousStepNumber>=0;){if(this.isStepPotentiallyVisible(this.getStepConfig(previousStepNumber)))return previousStepNumber;previousStepNumber--}return null}isLastStep(stepNumber){return null===this.getNextStepNumber(stepNumber)}isStepPotentiallyVisible(stepConfig){return!!stepConfig&&(!!this.isStepActuallyVisible(stepConfig)||(!(void 0===stepConfig.orphan||!stepConfig.orphan)||!(void 0===stepConfig.delay||!stepConfig.delay)))}getPotentiallyVisibleSteps(){let position=1,result=[];for(let stepNumber=0;stepNumber<this.steps.length;stepNumber++){const stepConfig=this.getStepConfig(stepNumber);this.isStepPotentiallyVisible(stepConfig)&&(result[stepNumber]={stepId:stepConfig.stepid,position:position},position++)}return result}isStepActuallyVisible(stepConfig){if(!stepConfig)return!1;if(!this.isCSSAllowed())return!1;let target=this.getStepTarget(stepConfig);return!!(target&&target.length&&target.is(":visible"))&&!!target.length}isCSSAllowed(){const testCSSElement=document.createElement("div");testCSSElement.classList.add("hide"),document.body.appendChild(testCSSElement);const isAllowed="none"===window.getComputedStyle(testCSSElement).display;return testCSSElement.remove(),isAllowed}next(){return this.gotoStep(this.getNextStepNumber())}previous(){return this.gotoStep(this.getPreviousStepNumber(),-1)}gotoStep(stepNumber,direction){if(stepNumber<0)return this.endTour();let stepConfig=this.getStepConfig(stepNumber);return null===stepConfig?this.endTour():this._gotoStep(stepConfig,direction)}_gotoStep(stepConfig,direction){if(!stepConfig)return this.endTour();if(void 0!==stepConfig.delay&&stepConfig.delay&&!stepConfig.delayed)return stepConfig.delayed=!0,window.setTimeout(this._gotoStep.bind(this),stepConfig.delay,stepConfig,direction),this;if(!stepConfig.orphan&&!this.isStepActuallyVisible(stepConfig)){let fn=-1==direction?"getPreviousStepNumber":"getNextStepNumber";return this.gotoStep(this[fn](stepConfig.stepNumber),direction)}this.hide();return this.dispatchEvent(_events.eventTypes.stepRender,{stepConfig:stepConfig},!0).defaultPrevented||(this.renderStep(stepConfig),this.dispatchEvent(_events.eventTypes.stepRendered,{stepConfig:stepConfig})),this}getStepConfig(stepNumber){if(null===stepNumber||stepNumber<0||stepNumber>=this.steps.length)return null;let stepConfig=this.normalizeStepConfig(this.steps[stepNumber]);return stepConfig=_jquery.default.extend(stepConfig,{stepNumber:stepNumber}),stepConfig}normalizeStepConfig(stepConfig){return void 0!==stepConfig.reflex&&void 0===stepConfig.moveAfterClick&&(stepConfig.moveAfterClick=stepConfig.reflex),void 0!==stepConfig.element&&void 0===stepConfig.target&&(stepConfig.target=stepConfig.element),void 0!==stepConfig.content&&void 0===stepConfig.body&&(stepConfig.body=stepConfig.content),stepConfig=_jquery.default.extend({},this.stepDefaults,stepConfig),(stepConfig=_jquery.default.extend({},{attachTo:stepConfig.target,attachPoint:"after"},stepConfig)).attachTo&&(stepConfig.attachTo=(0,_jquery.default)(stepConfig.attachTo).first()),stepConfig}getStepTarget(stepConfig){return stepConfig.target?(0,_jquery.default)(stepConfig.target):null}dispatchEvent(eventName){let detail=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},cancelable=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return(0,_event_dispatcher.dispatchEvent)(eventName,{tour:this,...detail},document,{cancelable:cancelable})}addEventHandler(eventName,handler){return void 0===this.eventHandlers[eventName]&&(this.eventHandlers[eventName]=[]),this.eventHandlers[eventName].push(handler),this}processStepListeners(stepConfig){if(this.listeners.push({node:this.currentStepNode,args:["click",'[data-role="next"]',_jquery.default.proxy(this.next,this)]},{node:this.currentStepNode,args:["click",'[data-role="end"]',_jquery.default.proxy(this.endTour,this)]},{node:(0,_jquery.default)('[data-flexitour="backdrop"]'),args:["click",_jquery.default.proxy(this.hide,this)]},{node:(0,_jquery.default)("body"),args:["keydown",_jquery.default.proxy(this.handleKeyDown,this)]}),stepConfig.moveOnClick){var targetNode=this.getStepTarget(stepConfig);this.listeners.push({node:targetNode,args:["click",_jquery.default.proxy((function(e){0===(0,_jquery.default)(e.target).parents('[data-flexitour="container"]').length&&window.setTimeout(_jquery.default.proxy(this.next,this),500)}),this)]})}return this.listeners.forEach((function(listener){listener.node.on.apply(listener.node,listener.args)})),this}resetStepListeners(){return this.listeners&&this.listeners.forEach((function(listener){listener.node.off.apply(listener.node,listener.args)})),this.listeners=[],this}renderStep(stepConfig){this.currentStepConfig=stepConfig,this.setCurrentStepNumber(stepConfig.stepNumber);let template=(0,_jquery.default)(this.getTemplateContent());template.find('[data-placeholder="title"]').html(stepConfig.title),template.find('[data-placeholder="body"]').html(stepConfig.body);const nextBtn=template.find('[data-role="next"]'),endBtn=template.find('[data-role="end"]');if(this.isLastStep(stepConfig.stepNumber)?(nextBtn.hide(),endBtn.removeClass("btn-secondary").addClass("btn-primary")):(nextBtn.prop("disabled",!1),(0,_str.get_string)("skip_tour","tool_usertours").then((value=>{endBtn.html(value)})).catch()),nextBtn.attr("role","button"),endBtn.attr("role","button"),this.originalConfiguration.displaystepnumbers){const stepsPotentiallyVisible=this.getPotentiallyVisibleSteps(),totalStepsPotentiallyVisible=stepsPotentiallyVisible.length,position=stepsPotentiallyVisible[stepConfig.stepNumber].position;totalStepsPotentiallyVisible>1&&(0,_str.get_string)("nextstep_sequence","tool_usertours",{position:position,total:totalStepsPotentiallyVisible}).then((value=>{nextBtn.html(value)})).catch()}return stepConfig.template=template,this.addStepToPage(stepConfig),this.processStepListeners(stepConfig),this}getTemplateContent(){return(0,_jquery.default)(this.templateContent).clone()}addStepToPage(stepConfig){let currentStepNode=(0,_jquery.default)('<span data-flexitour="container"></span>').html(stepConfig.template).hide(),animationTarget=(0,_jquery.default)("body, html").stop(!0,!0);if(this.isStepActuallyVisible(stepConfig)){let targetNode=this.getStepTarget(stepConfig);targetNode.parents('[data-usertour="scroller"]').length&&(animationTarget=targetNode.parents('[data-usertour="scroller"]')),targetNode.data("flexitour","target");let zIndex=this.calculateZIndex(targetNode);zIndex&&(stepConfig.zIndex=zIndex+1),stepConfig.zIndex&¤tStepNode.css("zIndex",stepConfig.zIndex+1),this.positionBackdrop(stepConfig),(0,_jquery.default)(document.body).append(currentStepNode),this.currentStepNode=currentStepNode,this.currentStepNode.css({top:0,left:0}),animationTarget.animate({scrollTop:this.calculateScrollTop(stepConfig)}).promise().then(function(){this.positionStep(stepConfig),this.revealStep(stepConfig)}.bind(this)).catch((function(){}))}else stepConfig.orphan&&(stepConfig.isOrphan=!0,stepConfig.attachTo=(0,_jquery.default)("body").first(),stepConfig.attachPoint="append",this.positionBackdrop(stepConfig),currentStepNode.addClass("orphan"),(0,_jquery.default)(document.body).append(currentStepNode),this.currentStepNode=currentStepNode,this.currentStepNode.css("position","fixed"),this.currentStepPopper=new _popper.default((0,_jquery.default)("body"),this.currentStepNode[0],{removeOnDestroy:!0,placement:stepConfig.placement+"-start",arrowElement:'[data-role="arrow"]',modifiers:{hide:{enabled:!1},applyStyle:{onLoad:null,enabled:!1}},onCreate:()=>{const images=this.currentStepNode.find("img");images.length&&images.on("load",(()=>{this.calculateStepPositionInPage(currentStepNode)})),this.calculateStepPositionInPage(currentStepNode)}}),this.revealStep(stepConfig));return this}revealStep(stepConfig){return this.currentStepNode.fadeIn("",_jquery.default.proxy((function(){this.announceStep(stepConfig),this.currentStepNode.focus(),window.setTimeout(_jquery.default.proxy((function(){this.currentStepNode&&this.currentStepNode.focus()}),this),100)}),this)),this}announceStep(stepConfig){let stepId="tour-step-"+this.tourName+"-"+stepConfig.stepNumber;this.currentStepNode.attr("id",stepId);let bodyRegion=this.currentStepNode.find('[data-placeholder="body"]').first();bodyRegion.attr("id",stepId+"-body"),bodyRegion.attr("role","document");let headerRegion=this.currentStepNode.find('[data-placeholder="title"]').first();headerRegion.attr("id",stepId+"-title"),headerRegion.attr("aria-labelledby",stepId+"-body"),this.currentStepNode.attr("role","dialog"),this.currentStepNode.attr("tabindex",0),this.currentStepNode.attr("aria-labelledby",stepId+"-title"),this.currentStepNode.attr("aria-describedby",stepId+"-body");let target=this.getStepTarget(stepConfig);return target&&(target.data("original-tabindex",target.attr("tabindex")),target.attr("tabindex")||target.attr("tabindex",0),target.data("original-describedby",target.attr("aria-describedby")).attr("aria-describedby",stepId+"-body")),this.accessibilityShow(stepConfig),this}handleKeyDown(e){let tabbableSelector="a[href], link[href], [draggable=true], [contenteditable=true], ";switch(tabbableSelector+=":input:enabled, [tabindex], button:enabled",e.keyCode){case 27:this.endTour();break;case 9:(function(){if(!this.currentStepConfig.hasBackdrop)return;let currentIndex,nextIndex,nextNode,focusRelevant,activeElement=(0,_jquery.default)(document.activeElement),stepTarget=this.getStepTarget(this.currentStepConfig),tabbableNodes=(0,_jquery.default)(tabbableSelector),dialogContainer=(0,_jquery.default)('span[data-flexitour="container"]');if(stepTarget&&(tabbableNodes=tabbableNodes.filter((function(index,element){return null!==stepTarget&&(stepTarget.has(element).length||dialogContainer.has(element).length||stepTarget.is(element)||dialogContainer.is(element))}))),tabbableNodes.each((function(index,element){return!activeElement.is(element)||(currentIndex=index,!1)})),null!=currentIndex){let direction=1;e.shiftKey&&(direction=-1),nextIndex=currentIndex;do{nextIndex+=direction,nextNode=(0,_jquery.default)(tabbableNodes[nextIndex])}while(nextNode.length&&nextNode.is(":disabled")||nextNode.is(":hidden"));nextNode.length?(focusRelevant=nextNode.closest(stepTarget).length,focusRelevant=focusRelevant||nextNode.closest(this.currentStepNode).length):focusRelevant=!1}focusRelevant?nextNode.focus():e.shiftKey?this.currentStepNode.find(tabbableSelector).last().focus():this.currentStepConfig.isOrphan?this.currentStepNode.focus():stepTarget.focus(),e.preventDefault()}).call(this)}}startTour(startAt){if(this.storage&&void 0===startAt){let storageStartValue=this.storage.getItem(this.storageKey);if(storageStartValue){let storageStartAt=parseInt(storageStartValue,10);storageStartAt<=this.steps.length&&(startAt=storageStartAt)}}void 0===startAt&&(startAt=this.getCurrentStepNumber());return this.dispatchEvent(_events.eventTypes.tourStart,{startAt:startAt},!0).defaultPrevented||(this.gotoStep(startAt),this.tourRunning=!0,this.dispatchEvent(_events.eventTypes.tourStarted,{startAt:startAt})),this}restartTour(){return this.startTour(0)}endTour(){if(this.dispatchEvent(_events.eventTypes.tourEnd,{},!0).defaultPrevented)return this;if(this.currentStepConfig){let previousTarget=this.getStepTarget(this.currentStepConfig);previousTarget&&(previousTarget.attr("tabindex")||previousTarget.attr("tabindex","-1"),previousTarget.focus())}return this.hide(!0),this.tourRunning=!1,this.dispatchEvent(_events.eventTypes.tourEnded),this}hide(transition){if(this.dispatchEvent(_events.eventTypes.stepHide,{},!0).defaultPrevented)return this;if(this.currentStepNode&&this.currentStepNode.length&&(this.currentStepNode.hide(),this.currentStepPopper&&this.currentStepPopper.destroy()),this.currentStepConfig){let target=this.getStepTarget(this.currentStepConfig);target&&(target.data("original-labelledby")&&target.attr("aria-labelledby",target.data("original-labelledby")),target.data("original-describedby")&&target.attr("aria-describedby",target.data("original-describedby")),target.data("original-tabindex")?target.attr("tabindex",target.data("tabindex")):window.setTimeout((()=>{target.removeAttr("tabindex")}),400)),this.currentStepConfig=null}let fadeTime=0;if(transition&&(fadeTime=400),(0,_jquery.default)('[data-flexitour="step-background"]').remove(),(0,_jquery.default)('[data-flexitour="step-backdrop"]').removeAttr("data-flexitour"),(0,_jquery.default)('[data-flexitour="backdrop"]').fadeOut(fadeTime,(function(){(0,_jquery.default)(this).remove()})),this.currentStepNode&&this.currentStepNode.length){let stepId=this.currentStepNode.attr("id");if(stepId){let currentStepElement='[aria-describedby="'+stepId+'-body"]';(0,_jquery.default)(currentStepElement).removeAttr("tabindex"),(0,_jquery.default)(currentStepElement).removeAttr("aria-describedby")}}return this.resetStepListeners(),this.accessibilityHide(),this.dispatchEvent(_events.eventTypes.stepHidden),this.currentStepNode=null,this.currentStepPopper=null,this}show(){let startAt=this.getCurrentStepNumber();return this.gotoStep(startAt)}getStepContainer(){return(0,_jquery.default)(this.currentStepNode)}calculateScrollTop(stepConfig){let viewportHeight=(0,_jquery.default)(window).height(),targetNode=this.getStepTarget(stepConfig),scrollParent=(0,_jquery.default)(window);targetNode.parents('[data-usertour="scroller"]').length&&(scrollParent=targetNode.parents('[data-usertour="scroller"]'));let scrollTop=scrollParent.scrollTop();return scrollTop="top"===stepConfig.placement?targetNode.offset().top-viewportHeight/2:"bottom"===stepConfig.placement?targetNode.offset().top+targetNode.height()+scrollTop-viewportHeight/2:targetNode.height()<=.8*viewportHeight?targetNode.offset().top-(viewportHeight-targetNode.height())/2:targetNode.offset().top-.2*viewportHeight,scrollTop=Math.max(0,scrollTop),scrollTop=Math.min((0,_jquery.default)(document).height()-viewportHeight,scrollTop),Math.ceil(scrollTop)}calculateStepPositionInPage(currentStepNode){let top=10;const viewportHeight=(0,_jquery.default)(window).height(),stepHeight=currentStepNode.height(),viewportWidth=(0,_jquery.default)(window).width(),stepWidth=currentStepNode.width();if(viewportHeight>=stepHeight+20)top=Math.ceil((viewportHeight-stepHeight)/2);else{var _currentStepNode$find,_currentStepNode$find2;const maxHeight=viewportHeight-20-(null!==(_currentStepNode$find=currentStepNode.find(".modal-header").first().outerHeight())&&void 0!==_currentStepNode$find?_currentStepNode$find:0)-(null!==(_currentStepNode$find2=currentStepNode.find(".modal-footer").first().outerHeight())&&void 0!==_currentStepNode$find2?_currentStepNode$find2:0);currentStepNode.find('[data-placeholder="body"]').first().css({"max-height":maxHeight+"px",overflow:"auto"})}currentStepNode.offset({top:top,left:Math.ceil((viewportWidth-stepWidth)/2)})}positionStep(stepConfig){let flipBehavior,content=this.currentStepNode,thisT=this;if(!content||!content.length)return this;switch(stepConfig.placement=this.recalculatePlacement(stepConfig),stepConfig.placement){case"left":flipBehavior=["left","right","top","bottom"];break;case"right":flipBehavior=["right","left","top","bottom"];break;case"top":flipBehavior=["top","bottom","right","left"];break;case"bottom":flipBehavior=["bottom","top","right","left"];break;default:flipBehavior="flip"}let target=this.getStepTarget(stepConfig);var config={placement:stepConfig.placement+"-start",removeOnDestroy:!0,modifiers:{flip:{behaviour:flipBehavior},arrow:{element:'[data-role="arrow"]'}},onCreate:function(data){recalculateArrowPosition(data),recalculateStepPosition(data)},onUpdate:function(data){recalculateArrowPosition(data),thisT.possitionNeedToBeRecalculated&&(thisT.recalculatedNo++,thisT.possitionNeedToBeRecalculated=!1,recalculateStepPosition(data))}};let recalculateArrowPosition=function(data){let placement=data.placement.split("-")[0];const isVertical=-1!==["left","right"].indexOf(placement),arrowElement=data.instance.popper.querySelector('[data-role="arrow"]'),stepElement=(0,_jquery.default)(data.instance.popper.querySelector('[data-role="flexitour-step"]'));if(isVertical){let arrowHeight=parseFloat(window.getComputedStyle(arrowElement).height),arrowOffset=parseFloat(window.getComputedStyle(arrowElement).top),popperHeight=parseFloat(window.getComputedStyle(data.instance.popper).height),popperOffset=parseFloat(window.getComputedStyle(data.instance.popper).top),popperBorderWidth=parseFloat(stepElement.css("borderTopWidth")),popperBorderRadiusWidth=2*parseFloat(stepElement.css("borderTopLeftRadius")),arrowPos=arrowOffset+arrowHeight/2,maxPos=popperHeight+popperOffset-popperBorderWidth-popperBorderRadiusWidth,minPos=popperOffset+popperBorderWidth+popperBorderRadiusWidth;if(arrowPos>=maxPos||arrowPos<=minPos){let newArrowPos=0;newArrowPos=arrowPos>popperHeight/2?maxPos-arrowHeight:minPos+arrowHeight,(0,_jquery.default)(arrowElement).css("top",newArrowPos)}}else{let arrowWidth=parseFloat(window.getComputedStyle(arrowElement).width),arrowOffset=parseFloat(window.getComputedStyle(arrowElement).left),popperWidth=parseFloat(window.getComputedStyle(data.instance.popper).width),popperOffset=parseFloat(window.getComputedStyle(data.instance.popper).left),popperBorderWidth=parseFloat(stepElement.css("borderTopWidth")),popperBorderRadiusWidth=2*parseFloat(stepElement.css("borderTopLeftRadius")),arrowPos=arrowOffset+arrowWidth/2,maxPos=popperWidth+popperOffset-popperBorderWidth-popperBorderRadiusWidth,minPos=popperOffset+popperBorderWidth+popperBorderRadiusWidth;if(arrowPos>=maxPos||arrowPos<=minPos){let newArrowPos=0;newArrowPos=arrowPos>popperWidth/2?maxPos-arrowWidth:minPos+arrowWidth,(0,_jquery.default)(arrowElement).css("left",newArrowPos)}}};const recalculateStepPosition=function(data){var _headerEle$outerHeigh,_footerEle$outerHeigh;const placement=data.placement.split("-")[0],isVertical=-1!==["left","right"].indexOf(placement),popperElement=(0,_jquery.default)(data.instance.popper),targetElement=(0,_jquery.default)(data.instance.reference),arrowElement=popperElement.find('[data-role="arrow"]'),stepElement=popperElement.find('[data-role="flexitour-step"]'),viewportHeight=(0,_jquery.default)(window).height(),viewportWidth=(0,_jquery.default)(window).width(),arrowHeight=parseFloat(arrowElement.outerHeight(!0)),popperHeight=parseFloat(popperElement.outerHeight(!0)),targetHeight=parseFloat(targetElement.outerHeight(!0)),arrowWidth=parseFloat(arrowElement.outerWidth(!0)),popperWidth=parseFloat(popperElement.outerWidth(!0)),targetWidth=parseFloat(targetElement.outerWidth(!0));let maxHeight;if(thisT.recalculatedNo>1&&(thisT.currentStepPopper.options.placement=isVertical?"auto-left":"auto-bottom"),thisT.recalculatedNo>2)return;if(isVertical){const leftSpace=targetElement.offset().left>0?targetElement.offset().left:0,rightSpace=viewportWidth-leftSpace-targetWidth,remainingSpace=leftSpace>=rightSpace?leftSpace:rightSpace;if(maxHeight=viewportHeight-20,remainingSpace<popperWidth+arrowWidth){const maxWidth=remainingSpace-10-arrowWidth;maxWidth>0&&(popperElement.css({"max-width":maxWidth+"px"}),thisT.possitionNeedToBeRecalculated=!0)}else maxHeight<popperHeight&&popperElement.css({"max-height":maxHeight+"px"})}else{const topSpace=targetElement.offset().top>0?targetElement.offset().top:0,bottomSpace=viewportHeight-topSpace-targetHeight,remainingSpace=topSpace>=bottomSpace?topSpace:bottomSpace;maxHeight=remainingSpace-10-arrowHeight,remainingSpace<popperHeight+arrowHeight&&(thisT.possitionNeedToBeRecalculated=!0)}const currentStepBody=stepElement.find('[data-placeholder="body"]').first(),headerEle=stepElement.find(".modal-header").first(),footerEle=stepElement.find(".modal-footer").first();maxHeight=maxHeight-(null!==(_headerEle$outerHeigh=headerEle.outerHeight(!0))&&void 0!==_headerEle$outerHeigh?_headerEle$outerHeigh:0)-(null!==(_footerEle$outerHeigh=footerEle.outerHeight(!0))&&void 0!==_footerEle$outerHeigh?_footerEle$outerHeigh:0),maxHeight>0?(headerEle.removeClass("minimal"),footerEle.removeClass("minimal"),currentStepBody.css({"max-height":maxHeight+"px",overflow:"auto"})):(headerEle.addClass("minimal"),footerEle.addClass("minimal")),thisT.currentStepPopper.update()};let background=(0,_jquery.default)('[data-flexitour="step-background"]');return background.length&&(target=background),this.currentStepPopper=new _popper.default(target,content[0],config),this}recalculatePlacement(stepConfig){let target=this.getStepTarget(stepConfig),widthContent=this.currentStepNode.width()+16,targetOffsetLeft=target.offset().left-10,targetOffsetRight=target.offset().left+target.width()+10,placement=stepConfig.placement;return-1!==["left","right"].indexOf(placement)&&targetOffsetLeft<widthContent+10&&targetOffsetRight+widthContent+10>document.documentElement.clientWidth&&(placement="top"),placement}positionBackdrop(stepConfig){if(stepConfig.backdrop){this.currentStepConfig.hasBackdrop=!0;let backdrop=(0,_jquery.default)('<div data-flexitour="backdrop"></div>');if(stepConfig.zIndex?"append"===stepConfig.attachPoint?stepConfig.attachTo.append(backdrop):backdrop.insertAfter(stepConfig.attachTo):(0,_jquery.default)("body").append(backdrop),this.isStepActuallyVisible(stepConfig)){let background=(0,_jquery.default)('[data-flexitour="step-background"]');background.length||(background=(0,_jquery.default)('<div data-flexitour="step-background"></div>'));let targetNode=this.getStepTarget(stepConfig),buffer=10,colorNode=targetNode;buffer&&(colorNode=(0,_jquery.default)("body"));let drawertop=0;if(targetNode.parents('[data-usertour="scroller"]').length){const scrollerElement=targetNode.parents('[data-usertour="scroller"]'),navigationBuffer=scrollerElement.offset().top;scrollerElement.scrollTop()>=navigationBuffer&&(drawertop=scrollerElement.scrollTop()-navigationBuffer,background.css({position:"fixed"}))}background.css({width:targetNode.outerWidth()+buffer+buffer,height:targetNode.outerHeight()+buffer+buffer,left:targetNode.offset().left-buffer,top:targetNode.offset().top+drawertop-buffer,backgroundColor:this.calculateInherittedBackgroundColor(colorNode)}),targetNode.offset().left<buffer&&background.css({width:targetNode.outerWidth()+targetNode.offset().left+buffer,left:targetNode.offset().left}),targetNode.offset().top+drawertop<buffer&&background.css({height:targetNode.outerHeight()+targetNode.offset().top+buffer,top:targetNode.offset().top});let targetRadius=targetNode.css("borderRadius");targetRadius&&targetRadius!==(0,_jquery.default)("body").css("borderRadius")&&background.css("borderRadius",targetRadius),"absolute"===this.calculatePosition(targetNode)&&background.css("position","fixed");let fader=background.clone();if(fader.css({backgroundColor:backdrop.css("backgroundColor"),opacity:backdrop.css("opacity")}),fader.attr("data-flexitour","step-background-fader"),targetNode.parents('[data-region="fixed-drawer"]').length){let targetClone=targetNode.clone();background.append(targetClone)}stepConfig.zIndex?"append"===stepConfig.attachPoint?stepConfig.attachTo.append(background):(fader.insertAfter(stepConfig.attachTo),background.insertAfter(stepConfig.attachTo)):((0,_jquery.default)("body").append(fader),(0,_jquery.default)("body").append(background)),targetNode.attr("data-flexitour","step-backdrop"),stepConfig.zIndex&&(backdrop.css("zIndex",stepConfig.zIndex),background.css("zIndex",stepConfig.zIndex+1),targetNode.css("zIndex",stepConfig.zIndex+2)),fader.fadeOut("2000",(function(){(0,_jquery.default)(this).remove()}))}}return this}calculateZIndex(elem){for(elem=(0,_jquery.default)(elem);elem.length&&elem[0]!==document;){let position=elem.css("position");if("absolute"===position||"relative"===position||"fixed"===position){let value=parseInt(elem.css("zIndex"),10);if(!isNaN(value)&&0!==value)return value}elem=elem.parent()}return 0}calculateInherittedBackgroundColor(elem){let fakeNode=(0,_jquery.default)("<div>").hide();(0,_jquery.default)("body").append(fakeNode);let fakeElemColor=fakeNode.css("backgroundColor");for(fakeNode.remove(),elem=(0,_jquery.default)(elem);elem.length&&elem[0]!==document;){let color=elem.css("backgroundColor");if(color!==fakeElemColor)return color;elem=elem.parent()}return null}calculatePosition(elem){for(elem=(0,_jquery.default)(elem);elem.length&&elem[0]!==document;){let position=elem.css("position");if("static"!==position)return position;elem=elem.parent()}return null}accessibilityShow(){let hideFunction=function(child){let flexitourRole=child.data("flexitour");if(flexitourRole)switch(flexitourRole){case"container":case"target":return}child.attr("aria-hidden")||(child.attr("data-has-hidden",!0),Aria.hide(child))};this.currentStepNode.siblings().each((function(index,node){hideFunction((0,_jquery.default)(node))})),this.currentStepNode.parentsUntil("body").siblings().each((function(index,node){hideFunction((0,_jquery.default)(node))}))}accessibilityHide(){(0,_jquery.default)("[data-has-hidden]").each((function(index,node){var child;void 0!==(child=(0,_jquery.default)(node)).attr("data-has-hidden")&&(child.removeAttr("data-has-hidden"),Aria.unhide(child))}))}};return _exports.default=_default,_exports.default})); //# sourceMappingURL=tour.min.js.map build/filter_cssselector.min.js 0000644 00000000574 15152263317 0012674 0 ustar 00 define("tool_usertours/filter_cssselector",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.filterMatches=void 0;_exports.filterMatches=function(tourConfig){let filterValues=tourConfig.filtervalues.cssselector;return!filterValues[0]||!!document.querySelector(filterValues[0])}})); //# sourceMappingURL=filter_cssselector.min.js.map build/managetours.min.js 0000644 00000001721 15152263317 0011316 0 ustar 00 define("tool_usertours/managetours",["exports","core/prefetch","core/str","core/notification"],(function(_exports,_prefetch,_str,_notification){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setup=void 0;const removeTourHandler=e=>{const deleteButton=e.target.closest('[data-action="delete"]');deleteButton&&(e.preventDefault(),removeTourFromLink(deleteButton.href))},removeTourFromLink=targetUrl=>{(0,_notification.confirm)((0,_str.get_string)("confirmtourremovaltitle","tool_usertours"),(0,_str.get_string)("confirmtourremovalquestion","tool_usertours"),(0,_str.get_string)("yes","core"),(0,_str.get_string)("no","core"),(()=>{window.location=targetUrl}))};_exports.setup=()=>{(0,_prefetch.prefetchStrings)("tool_usertours",["confirmtourremovaltitle","confirmtourremovalquestion"]),(0,_prefetch.prefetchStrings)("core",["yes","no"]),document.querySelector("body").addEventListener("click",removeTourHandler)}})); //# sourceMappingURL=managetours.min.js.map src/usertours.js 0000644 00000016564 15152263317 0007765 0 ustar 00 /** * User tour control library. * * @module tool_usertours/usertours * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk> */ import BootstrapTour from './tour'; import Templates from 'core/templates'; import log from 'core/log'; import notification from 'core/notification'; import * as tourRepository from './repository'; import Pending from 'core/pending'; import {eventTypes} from './events'; let currentTour = null; let tourId = null; let restartTourAndKeepProgress = false; let currentStepNo = null; /** * Find the first matching tour. * * @param {object[]} tourDetails * @param {object[]} filters * @returns {null|object} */ const findMatchingTour = (tourDetails, filters) => { return tourDetails.find(tour => filters.some(filter => { if (filter && filter.filterMatches) { return filter.filterMatches(tour); } return true; })); }; /** * Initialise the user tour for the current page. * * @method init * @param {Array} tourDetails The matching tours for this page. * @param {Array} filters The names of all client side filters. */ export const init = async(tourDetails, filters) => { const requirements = []; filters.forEach(filter => { requirements.push(import(`tool_usertours/filter_${filter}`)); }); const filterPlugins = await Promise.all(requirements); const matchingTour = findMatchingTour(tourDetails, filterPlugins); if (!matchingTour) { return; } // Only one tour per page is allowed. tourId = matchingTour.tourId; let startTour = matchingTour.startTour; if (typeof startTour === 'undefined') { startTour = true; } if (startTour) { // Fetch the tour configuration. fetchTour(tourId); } addResetLink(); // Watch for the reset link. document.querySelector('body').addEventListener('click', e => { const resetLink = e.target.closest('#resetpagetour'); if (resetLink) { e.preventDefault(); resetTourState(tourId); } }); // Watch for the resize event. window.addEventListener("resize", () => { // Only listen for the running tour. if (currentTour && currentTour.tourRunning) { clearTimeout(window.resizedFinished); window.resizedFinished = setTimeout(() => { // Wait until the resize event has finished. currentStepNo = currentTour.getCurrentStepNumber(); restartTourAndKeepProgress = true; resetTourState(tourId); }, 250); } }); }; /** * Fetch the configuration specified tour, and start the tour when it has been fetched. * * @method fetchTour * @param {Number} tourId The ID of the tour to start. */ const fetchTour = async tourId => { const pendingPromise = new Pending(`admin_usertour_fetchTour:${tourId}`); try { // If we don't have any tour config (because it doesn't need showing for the current user), return early. const response = await tourRepository.fetchTour(tourId); if (response.hasOwnProperty('tourconfig')) { const {html} = await Templates.renderForPromise('tool_usertours/tourstep', response.tourconfig); startBootstrapTour(tourId, html, response.tourconfig); } pendingPromise.resolve(); } catch (error) { pendingPromise.resolve(); notification.exception(error); } }; const getPreferredResetLocation = () => { let location = document.querySelector('.tool_usertours-resettourcontainer'); if (location) { return location; } location = document.querySelector('.logininfo'); if (location) { return location; } location = document.querySelector('footer'); if (location) { return location; } return document.body; }; /** * Add a reset link to the page. * * @method addResetLink */ const addResetLink = () => { const pendingPromise = new Pending('admin_usertour_addResetLink'); Templates.render('tool_usertours/resettour', {}) .then(function(html, js) { // Append the link to the most suitable place on the page with fallback to legacy selectors and finally the body if // there is no better place. Templates.appendNodeContents(getPreferredResetLocation(), html, js); return; }) .catch() .then(pendingPromise.resolve) .catch(); }; /** * Start the specified tour. * * @method startBootstrapTour * @param {Number} tourId The ID of the tour to start. * @param {String} template The template to use. * @param {Object} tourConfig The tour configuration. * @return {Object} */ const startBootstrapTour = (tourId, template, tourConfig) => { if (currentTour && currentTour.tourRunning) { // End the current tour. currentTour.endTour(); currentTour = null; } document.addEventListener(eventTypes.tourEnded, markTourComplete); document.addEventListener(eventTypes.stepRenderer, markStepShown); // Sort out the tour name. tourConfig.tourName = tourConfig.name; delete tourConfig.name; // Add the template to the configuration. // This enables translations of the buttons. tourConfig.template = template; tourConfig.steps = tourConfig.steps.map(function(step) { if (typeof step.element !== 'undefined') { step.target = step.element; delete step.element; } if (typeof step.reflex !== 'undefined') { step.moveOnClick = !!step.reflex; delete step.reflex; } if (typeof step.content !== 'undefined') { step.body = step.content; delete step.content; } return step; }); currentTour = new BootstrapTour(tourConfig); let startAt = 0; if (restartTourAndKeepProgress && currentStepNo) { startAt = currentStepNo; restartTourAndKeepProgress = false; currentStepNo = null; } return currentTour.startTour(startAt); }; /** * Mark the specified step as being shownd by the user. * * @method markStepShown * @param {Event} e */ const markStepShown = e => { const tour = e.detail.tour; const stepConfig = tour.getStepConfig(tour.getCurrentStepNumber()); tourRepository.markStepShown( stepConfig.stepid, tourId, tour.getCurrentStepNumber() ).catch(log.error); }; /** * Mark the specified tour as being completed by the user. * * @method markTourComplete * @param {Event} e * @listens tool_usertours/stepRendered */ const markTourComplete = e => { document.removeEventListener(eventTypes.tourEnded, markTourComplete); document.removeEventListener(eventTypes.stepRenderer, markStepShown); const tour = e.detail.tour; const stepConfig = tour.getStepConfig(tour.getCurrentStepNumber()); tourRepository.markTourComplete( stepConfig.stepid, tourId, tour.getCurrentStepNumber() ).catch(log.error); }; /** * Reset the state, and restart the the tour on the current page. * * @method resetTourState * @param {Number} tourId The ID of the tour to start. * @returns {Promise} */ export const resetTourState = tourId => tourRepository.resetTourState(tourId) .then(response => { if (response.startTour) { fetchTour(response.startTour); } return; }).catch(notification.exception); src/repository.js 0000644 00000003316 15152263317 0010120 0 ustar 00 /** * Step management code. * * @module tool_usertours/managesteps * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk> */ import {call as fetchMany} from 'core/ajax'; import moodleConfig from 'core/config'; /** * Reset the tour state of the specified tour. * * @param {number} tourid * @return {Promise} */ export const resetTourState = tourid => fetchMany([{ methodname: 'tool_usertours_reset_tour', args: { tourid, context: moodleConfig.contextid, pageurl: window.location.href, } }])[0]; /** * Mark the specified tour as complete. * * @param {number} stepid * @param {number} tourid * @param {number} stepindex * @return {Promise} */ export const markTourComplete = (stepid, tourid, stepindex) => fetchMany([{ methodname: 'tool_usertours_complete_tour', args: { stepid, stepindex: stepindex, tourid, context: moodleConfig.contextid, pageurl: window.location.href, } }])[0]; /** * Fetch the specified tour. * * @param {number} tourid * @return {Promise} */ export const fetchTour = tourid => fetchMany([{ methodname: 'tool_usertours_fetch_and_start_tour', args: { tourid, context: moodleConfig.contextid, pageurl: window.location.href, } }])[0]; /** * Mark the specified step as having been shown. * * @param {number} stepid * @param {number} tourid * @param {number} stepindex * @return {Promise} */ export const markStepShown = (stepid, tourid, stepindex) => fetchMany([{ methodname: 'tool_usertours_step_shown', args: { tourid, stepid, stepindex, context: moodleConfig.contextid, pageurl: window.location.href, } }])[0]; src/tour.js 0000644 00000173243 15152263320 0006673 0 ustar 00 // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * A user tour. * * @module tool_usertours/tour * @copyright 2018 Andrew Nicols <andrew@nicols.co.uk> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** * A list of steps. * * @typedef {Object[]} StepList * @property {Number} stepId The id of the step in the database * @property {Number} position The position of the step within the tour (zero-indexed) */ import $ from 'jquery'; import * as Aria from 'core/aria'; import Popper from 'core/popper'; import {dispatchEvent} from 'core/event_dispatcher'; import {eventTypes} from './events'; import {get_string as getString} from 'core/str'; import {prefetchStrings} from 'core/prefetch'; /** * The minimum spacing for tour step to display. * * @private * @constant * @type {number} */ const MINSPACING = 10; /** * A user tour. * * @class tool_usertours/tour * @property {boolean} tourRunning Whether the tour is currently running. */ const Tour = class { tourRunning = false; /** * @param {object} config The configuration object. */ constructor(config) { this.init(config); } /** * Initialise the tour. * * @method init * @param {Object} config The configuration object. * @chainable * @return {Object} this. */ init(config) { // Unset all handlers. this.eventHandlers = {}; // Reset the current tour states. this.reset(); // Store the initial configuration. this.originalConfiguration = config || {}; // Apply configuration. this.configure.apply(this, arguments); // Unset recalculate state. this.possitionNeedToBeRecalculated = false; // Unset recalculate count. this.recalculatedNo = 0; try { this.storage = window.sessionStorage; this.storageKey = 'tourstate_' + this.tourName; } catch (e) { this.storage = false; this.storageKey = ''; } prefetchStrings('tool_usertours', [ 'nextstep_sequence', 'skip_tour' ]); return this; } /** * Reset the current tour state. * * @method reset * @chainable * @return {Object} this. */ reset() { // Hide the current step. this.hide(); // Unset all handlers. this.eventHandlers = []; // Unset all listeners. this.resetStepListeners(); // Unset the original configuration. this.originalConfiguration = {}; // Reset the current step number and list of steps. this.steps = []; // Reset the current step number. this.currentStepNumber = 0; return this; } /** * Prepare tour configuration. * * @method configure * @param {Object} config The configuration object. * @chainable * @return {Object} this. */ configure(config) { if (typeof config === 'object') { // Tour name. if (typeof config.tourName !== 'undefined') { this.tourName = config.tourName; } // Set up eventHandlers. if (config.eventHandlers) { for (let eventName in config.eventHandlers) { config.eventHandlers[eventName].forEach(function(handler) { this.addEventHandler(eventName, handler); }, this); } } // Reset the step configuration. this.resetStepDefaults(true); // Configure the steps. if (typeof config.steps === 'object') { this.steps = config.steps; } if (typeof config.template !== 'undefined') { this.templateContent = config.template; } } // Check that we have enough to start the tour. this.checkMinimumRequirements(); return this; } /** * Check that the configuration meets the minimum requirements. * * @method checkMinimumRequirements */ checkMinimumRequirements() { // Need a tourName. if (!this.tourName) { throw new Error("Tour Name required"); } // Need a minimum of one step. if (!this.steps || !this.steps.length) { throw new Error("Steps must be specified"); } } /** * Reset step default configuration. * * @method resetStepDefaults * @param {Boolean} loadOriginalConfiguration Whether to load the original configuration supplied with the Tour. * @chainable * @return {Object} this. */ resetStepDefaults(loadOriginalConfiguration) { if (typeof loadOriginalConfiguration === 'undefined') { loadOriginalConfiguration = true; } this.stepDefaults = {}; if (!loadOriginalConfiguration || typeof this.originalConfiguration.stepDefaults === 'undefined') { this.setStepDefaults({}); } else { this.setStepDefaults(this.originalConfiguration.stepDefaults); } return this; } /** * Set the step defaults. * * @method setStepDefaults * @param {Object} stepDefaults The step defaults to apply to all steps * @chainable * @return {Object} this. */ setStepDefaults(stepDefaults) { if (!this.stepDefaults) { this.stepDefaults = {}; } $.extend( this.stepDefaults, { element: '', placement: 'top', delay: 0, moveOnClick: false, moveAfterTime: 0, orphan: false, direction: 1, }, stepDefaults ); return this; } /** * Retrieve the current step number. * * @method getCurrentStepNumber * @return {Number} The current step number */ getCurrentStepNumber() { return parseInt(this.currentStepNumber, 10); } /** * Store the current step number. * * @method setCurrentStepNumber * @param {Number} stepNumber The current step number * @chainable */ setCurrentStepNumber(stepNumber) { this.currentStepNumber = stepNumber; if (this.storage) { try { this.storage.setItem(this.storageKey, stepNumber); } catch (e) { if (e.code === DOMException.QUOTA_EXCEEDED_ERR) { this.storage.removeItem(this.storageKey); } } } } /** * Get the next step number after the currently displayed step. * * @method getNextStepNumber * @param {Number} stepNumber The current step number * @return {Number} The next step number to display */ getNextStepNumber(stepNumber) { if (typeof stepNumber === 'undefined') { stepNumber = this.getCurrentStepNumber(); } let nextStepNumber = stepNumber + 1; // Keep checking the remaining steps. while (nextStepNumber <= this.steps.length) { if (this.isStepPotentiallyVisible(this.getStepConfig(nextStepNumber))) { return nextStepNumber; } nextStepNumber++; } return null; } /** * Get the previous step number before the currently displayed step. * * @method getPreviousStepNumber * @param {Number} stepNumber The current step number * @return {Number} The previous step number to display */ getPreviousStepNumber(stepNumber) { if (typeof stepNumber === 'undefined') { stepNumber = this.getCurrentStepNumber(); } let previousStepNumber = stepNumber - 1; // Keep checking the remaining steps. while (previousStepNumber >= 0) { if (this.isStepPotentiallyVisible(this.getStepConfig(previousStepNumber))) { return previousStepNumber; } previousStepNumber--; } return null; } /** * Is the step the final step number? * * @method isLastStep * @param {Number} stepNumber Step number to test * @return {Boolean} Whether the step is the final step */ isLastStep(stepNumber) { let nextStepNumber = this.getNextStepNumber(stepNumber); return nextStepNumber === null; } /** * Is this step potentially visible? * * @method isStepPotentiallyVisible * @param {Object} stepConfig The step configuration to normalise * @return {Boolean} Whether the step is the potentially visible */ isStepPotentiallyVisible(stepConfig) { if (!stepConfig) { // Without step config, there can be no step. return false; } if (this.isStepActuallyVisible(stepConfig)) { // If it is actually visible, it is already potentially visible. return true; } if (typeof stepConfig.orphan !== 'undefined' && stepConfig.orphan) { // Orphan steps have no target. They are always visible. return true; } if (typeof stepConfig.delay !== 'undefined' && stepConfig.delay) { // Only return true if the activated has not been used yet. return true; } // Not theoretically, or actually visible. return false; } /** * Get potentially visible steps in a tour. * * @returns {StepList} A list of ordered steps */ getPotentiallyVisibleSteps() { let position = 1; let result = []; // Checking the total steps. for (let stepNumber = 0; stepNumber < this.steps.length; stepNumber++) { const stepConfig = this.getStepConfig(stepNumber); if (this.isStepPotentiallyVisible(stepConfig)) { result[stepNumber] = {stepId: stepConfig.stepid, position: position}; position++; } } return result; } /** * Is this step actually visible? * * @method isStepActuallyVisible * @param {Object} stepConfig The step configuration to normalise * @return {Boolean} Whether the step is actually visible */ isStepActuallyVisible(stepConfig) { if (!stepConfig) { // Without step config, there can be no step. return false; } // Check if the CSS styles are allowed on the browser or not. if (!this.isCSSAllowed()) { return false; } let target = this.getStepTarget(stepConfig); if (target && target.length && target.is(':visible')) { // Without a target, there can be no step. return !!target.length; } return false; } /** * Is the browser actually allow CSS styles? * * @returns {boolean} True if the browser is allowing CSS styles */ isCSSAllowed() { const testCSSElement = document.createElement('div'); testCSSElement.classList.add('hide'); document.body.appendChild(testCSSElement); const styles = window.getComputedStyle(testCSSElement); const isAllowed = styles.display === 'none'; testCSSElement.remove(); return isAllowed; } /** * Go to the next step in the tour. * * @method next * @chainable * @return {Object} this. */ next() { return this.gotoStep(this.getNextStepNumber()); } /** * Go to the previous step in the tour. * * @method previous * @chainable * @return {Object} this. */ previous() { return this.gotoStep(this.getPreviousStepNumber(), -1); } /** * Go to the specified step in the tour. * * @method gotoStep * @param {Number} stepNumber The step number to display * @param {Number} direction Next or previous step * @chainable * @return {Object} this. * @fires tool_usertours/stepRender * @fires tool_usertours/stepRendered * @fires tool_usertours/stepHide * @fires tool_usertours/stepHidden */ gotoStep(stepNumber, direction) { if (stepNumber < 0) { return this.endTour(); } let stepConfig = this.getStepConfig(stepNumber); if (stepConfig === null) { return this.endTour(); } return this._gotoStep(stepConfig, direction); } _gotoStep(stepConfig, direction) { if (!stepConfig) { return this.endTour(); } if (typeof stepConfig.delay !== 'undefined' && stepConfig.delay && !stepConfig.delayed) { stepConfig.delayed = true; window.setTimeout(this._gotoStep.bind(this), stepConfig.delay, stepConfig, direction); return this; } else if (!stepConfig.orphan && !this.isStepActuallyVisible(stepConfig)) { let fn = direction == -1 ? 'getPreviousStepNumber' : 'getNextStepNumber'; return this.gotoStep(this[fn](stepConfig.stepNumber), direction); } this.hide(); const stepRenderEvent = this.dispatchEvent(eventTypes.stepRender, {stepConfig}, true); if (!stepRenderEvent.defaultPrevented) { this.renderStep(stepConfig); this.dispatchEvent(eventTypes.stepRendered, {stepConfig}); } return this; } /** * Fetch the normalised step configuration for the specified step number. * * @method getStepConfig * @param {Number} stepNumber The step number to fetch configuration for * @return {Object} The step configuration */ getStepConfig(stepNumber) { if (stepNumber === null || stepNumber < 0 || stepNumber >= this.steps.length) { return null; } // Normalise the step configuration. let stepConfig = this.normalizeStepConfig(this.steps[stepNumber]); // Add the stepNumber to the stepConfig. stepConfig = $.extend(stepConfig, {stepNumber: stepNumber}); return stepConfig; } /** * Normalise the supplied step configuration. * * @method normalizeStepConfig * @param {Object} stepConfig The step configuration to normalise * @return {Object} The normalised step configuration */ normalizeStepConfig(stepConfig) { if (typeof stepConfig.reflex !== 'undefined' && typeof stepConfig.moveAfterClick === 'undefined') { stepConfig.moveAfterClick = stepConfig.reflex; } if (typeof stepConfig.element !== 'undefined' && typeof stepConfig.target === 'undefined') { stepConfig.target = stepConfig.element; } if (typeof stepConfig.content !== 'undefined' && typeof stepConfig.body === 'undefined') { stepConfig.body = stepConfig.content; } stepConfig = $.extend({}, this.stepDefaults, stepConfig); stepConfig = $.extend({}, { attachTo: stepConfig.target, attachPoint: 'after', }, stepConfig); if (stepConfig.attachTo) { stepConfig.attachTo = $(stepConfig.attachTo).first(); } return stepConfig; } /** * Fetch the actual step target from the selector. * * This should not be called until after any delay has completed. * * @method getStepTarget * @param {Object} stepConfig The step configuration * @return {$} */ getStepTarget(stepConfig) { if (stepConfig.target) { return $(stepConfig.target); } return null; } /** * Fire any event handlers for the specified event. * * @param {String} eventName The name of the event * @param {Object} [detail={}] Any additional details to pass into the eveent * @param {Boolean} [cancelable=false] Whether preventDefault() can be called * @returns {CustomEvent} */ dispatchEvent( eventName, detail = {}, cancelable = false ) { return dispatchEvent(eventName, { // Add the tour to the detail. tour: this, ...detail, }, document, { cancelable, }); } /** * @method addEventHandler * @param {string} eventName The name of the event to listen for * @param {function} handler The event handler to call * @return {Object} this. */ addEventHandler(eventName, handler) { if (typeof this.eventHandlers[eventName] === 'undefined') { this.eventHandlers[eventName] = []; } this.eventHandlers[eventName].push(handler); return this; } /** * Process listeners for the step being shown. * * @method processStepListeners * @param {object} stepConfig The configuration for the step * @chainable * @return {Object} this. */ processStepListeners(stepConfig) { this.listeners.push( // Next button. { node: this.currentStepNode, args: ['click', '[data-role="next"]', $.proxy(this.next, this)] }, // Close and end tour buttons. { node: this.currentStepNode, args: ['click', '[data-role="end"]', $.proxy(this.endTour, this)] }, // Click backdrop and hide tour. { node: $('[data-flexitour="backdrop"]'), args: ['click', $.proxy(this.hide, this)] }, // Keypresses. { node: $('body'), args: ['keydown', $.proxy(this.handleKeyDown, this)] }); if (stepConfig.moveOnClick) { var targetNode = this.getStepTarget(stepConfig); this.listeners.push({ node: targetNode, args: ['click', $.proxy(function(e) { if ($(e.target).parents('[data-flexitour="container"]').length === 0) { // Ignore clicks when they are in the flexitour. window.setTimeout($.proxy(this.next, this), 500); } }, this)] }); } this.listeners.forEach(function(listener) { listener.node.on.apply(listener.node, listener.args); }); return this; } /** * Reset step listeners. * * @method resetStepListeners * @chainable * @return {Object} this. */ resetStepListeners() { // Stop listening to all external handlers. if (this.listeners) { this.listeners.forEach(function(listener) { listener.node.off.apply(listener.node, listener.args); }); } this.listeners = []; return this; } /** * The standard step renderer. * * @method renderStep * @param {Object} stepConfig The step configuration of the step * @chainable * @return {Object} this. */ renderStep(stepConfig) { // Store the current step configuration for later. this.currentStepConfig = stepConfig; this.setCurrentStepNumber(stepConfig.stepNumber); // Fetch the template and convert it to a $ object. let template = $(this.getTemplateContent()); // Title. template.find('[data-placeholder="title"]') .html(stepConfig.title); // Body. template.find('[data-placeholder="body"]') .html(stepConfig.body); // Buttons. const nextBtn = template.find('[data-role="next"]'); const endBtn = template.find('[data-role="end"]'); // Is this the final step? if (this.isLastStep(stepConfig.stepNumber)) { nextBtn.hide(); endBtn.removeClass("btn-secondary").addClass("btn-primary"); } else { nextBtn.prop('disabled', false); // Use Skip tour label for the End tour button. getString('skip_tour', 'tool_usertours').then(value => { endBtn.html(value); return; }).catch(); } nextBtn.attr('role', 'button'); endBtn.attr('role', 'button'); if (this.originalConfiguration.displaystepnumbers) { const stepsPotentiallyVisible = this.getPotentiallyVisibleSteps(); const totalStepsPotentiallyVisible = stepsPotentiallyVisible.length; const position = stepsPotentiallyVisible[stepConfig.stepNumber].position; if (totalStepsPotentiallyVisible > 1) { // Change the label of the Next button to include the sequence. getString('nextstep_sequence', 'tool_usertours', {position: position, total: totalStepsPotentiallyVisible}).then(value => { nextBtn.html(value); return; }).catch(); } } // Replace the template with the updated version. stepConfig.template = template; // Add to the page. this.addStepToPage(stepConfig); // Process step listeners after adding to the page. // This uses the currentNode. this.processStepListeners(stepConfig); return this; } /** * Getter for the template content. * * @method getTemplateContent * @return {$} */ getTemplateContent() { return $(this.templateContent).clone(); } /** * Helper to add a step to the page. * * @method addStepToPage * @param {Object} stepConfig The step configuration of the step * @chainable * @return {Object} this. */ addStepToPage(stepConfig) { // Create the stepNode from the template data. let currentStepNode = $('<span data-flexitour="container"></span>') .html(stepConfig.template) .hide(); // The scroll animation occurs on the body or html. let animationTarget = $('body, html') .stop(true, true); if (this.isStepActuallyVisible(stepConfig)) { let targetNode = this.getStepTarget(stepConfig); if (targetNode.parents('[data-usertour="scroller"]').length) { animationTarget = targetNode.parents('[data-usertour="scroller"]'); } targetNode.data('flexitour', 'target'); let zIndex = this.calculateZIndex(targetNode); if (zIndex) { stepConfig.zIndex = zIndex + 1; } if (stepConfig.zIndex) { currentStepNode.css('zIndex', stepConfig.zIndex + 1); } // Add the backdrop. this.positionBackdrop(stepConfig); $(document.body).append(currentStepNode); this.currentStepNode = currentStepNode; // Ensure that the step node is positioned. // Some situations mean that the value is not properly calculated without this step. this.currentStepNode.css({ top: 0, left: 0, }); animationTarget .animate({ scrollTop: this.calculateScrollTop(stepConfig), }).promise().then(function() { this.positionStep(stepConfig); this.revealStep(stepConfig); return; }.bind(this)) .catch(function() { // Silently fail. }); } else if (stepConfig.orphan) { stepConfig.isOrphan = true; // This will be appended to the body instead. stepConfig.attachTo = $('body').first(); stepConfig.attachPoint = 'append'; // Add the backdrop. this.positionBackdrop(stepConfig); // This is an orphaned step. currentStepNode.addClass('orphan'); // It lives in the body. $(document.body).append(currentStepNode); this.currentStepNode = currentStepNode; this.currentStepNode.css('position', 'fixed'); this.currentStepPopper = new Popper( $('body'), this.currentStepNode[0], { removeOnDestroy: true, placement: stepConfig.placement + '-start', arrowElement: '[data-role="arrow"]', // Empty the modifiers. We've already placed the step and don't want it moved. modifiers: { hide: { enabled: false, }, applyStyle: { onLoad: null, enabled: false, }, }, onCreate: () => { // First, we need to check if the step's content contains any images. const images = this.currentStepNode.find('img'); if (images.length) { // Images found, need to calculate the position when the image is loaded. images.on('load', () => { this.calculateStepPositionInPage(currentStepNode); }); } this.calculateStepPositionInPage(currentStepNode); } } ); this.revealStep(stepConfig); } return this; } /** * Make the given step visible. * * @method revealStep * @param {Object} stepConfig The step configuration of the step * @chainable * @return {Object} this. */ revealStep(stepConfig) { // Fade the step in. this.currentStepNode.fadeIn('', $.proxy(function() { // Announce via ARIA. this.announceStep(stepConfig); // Focus on the current step Node. this.currentStepNode.focus(); window.setTimeout($.proxy(function() { // After a brief delay, focus again. // There seems to be an issue with Jaws where it only reads the dialogue title initially. // This second focus helps it to read the full dialogue. if (this.currentStepNode) { this.currentStepNode.focus(); } }, this), 100); }, this)); return this; } /** * Helper to announce the step on the page. * * @method announceStep * @param {Object} stepConfig The step configuration of the step * @chainable * @return {Object} this. */ announceStep(stepConfig) { // Setup the step Dialogue as per: // * https://www.w3.org/TR/wai-aria-practices/#dialog_nonmodal // * https://www.w3.org/TR/wai-aria-practices/#dialog_modal // Generate an ID for the current step node. let stepId = 'tour-step-' + this.tourName + '-' + stepConfig.stepNumber; this.currentStepNode.attr('id', stepId); let bodyRegion = this.currentStepNode.find('[data-placeholder="body"]').first(); bodyRegion.attr('id', stepId + '-body'); bodyRegion.attr('role', 'document'); let headerRegion = this.currentStepNode.find('[data-placeholder="title"]').first(); headerRegion.attr('id', stepId + '-title'); headerRegion.attr('aria-labelledby', stepId + '-body'); // Generally, a modal dialog has a role of dialog. this.currentStepNode.attr('role', 'dialog'); this.currentStepNode.attr('tabindex', 0); this.currentStepNode.attr('aria-labelledby', stepId + '-title'); this.currentStepNode.attr('aria-describedby', stepId + '-body'); // Configure ARIA attributes on the target. let target = this.getStepTarget(stepConfig); if (target) { target.data('original-tabindex', target.attr('tabindex')); if (!target.attr('tabindex')) { target.attr('tabindex', 0); } target .data('original-describedby', target.attr('aria-describedby')) .attr('aria-describedby', stepId + '-body') ; } this.accessibilityShow(stepConfig); return this; } /** * Handle key down events. * * @method handleKeyDown * @param {EventFacade} e */ handleKeyDown(e) { let tabbableSelector = 'a[href], link[href], [draggable=true], [contenteditable=true], '; tabbableSelector += ':input:enabled, [tabindex], button:enabled'; switch (e.keyCode) { case 27: this.endTour(); break; // 9 == Tab - trap focus for items with a backdrop. case 9: // Tab must be handled on key up only in this instance. (function() { if (!this.currentStepConfig.hasBackdrop) { // Trapping tab focus is only handled for those steps with a backdrop. return; } // Find all tabbable locations. let activeElement = $(document.activeElement); let stepTarget = this.getStepTarget(this.currentStepConfig); let tabbableNodes = $(tabbableSelector); let dialogContainer = $('span[data-flexitour="container"]'); let currentIndex; // Filter out element which is not belong to target section or dialogue. if (stepTarget) { tabbableNodes = tabbableNodes.filter(function(index, element) { return stepTarget !== null && (stepTarget.has(element).length || dialogContainer.has(element).length || stepTarget.is(element) || dialogContainer.is(element)); }); } // Find index of focusing element. tabbableNodes.each(function(index, element) { if (activeElement.is(element)) { currentIndex = index; return false; } // Keep looping. return true; }); let nextIndex; let nextNode; let focusRelevant; if (currentIndex != void 0) { let direction = 1; if (e.shiftKey) { direction = -1; } nextIndex = currentIndex; do { nextIndex += direction; nextNode = $(tabbableNodes[nextIndex]); } while (nextNode.length && nextNode.is(':disabled') || nextNode.is(':hidden')); if (nextNode.length) { // A new f focusRelevant = nextNode.closest(stepTarget).length; focusRelevant = focusRelevant || nextNode.closest(this.currentStepNode).length; } else { // Unable to find the target somehow. focusRelevant = false; } } if (focusRelevant) { nextNode.focus(); } else { if (e.shiftKey) { // Focus on the last tabbable node in the step. this.currentStepNode.find(tabbableSelector).last().focus(); } else { if (this.currentStepConfig.isOrphan) { // Focus on the step - there is no target. this.currentStepNode.focus(); } else { // Focus on the step target. stepTarget.focus(); } } } e.preventDefault(); }).call(this); break; } } /** * Start the current tour. * * @method startTour * @param {Number} startAt Which step number to start at. If not specified, starts at the last point. * @chainable * @return {Object} this. * @fires tool_usertours/tourStart * @fires tool_usertours/tourStarted */ startTour(startAt) { if (this.storage && typeof startAt === 'undefined') { let storageStartValue = this.storage.getItem(this.storageKey); if (storageStartValue) { let storageStartAt = parseInt(storageStartValue, 10); if (storageStartAt <= this.steps.length) { startAt = storageStartAt; } } } if (typeof startAt === 'undefined') { startAt = this.getCurrentStepNumber(); } const tourStartEvent = this.dispatchEvent(eventTypes.tourStart, {startAt}, true); if (!tourStartEvent.defaultPrevented) { this.gotoStep(startAt); this.tourRunning = true; this.dispatchEvent(eventTypes.tourStarted, {startAt}); } return this; } /** * Restart the tour from the beginning, resetting the completionlag. * * @method restartTour * @chainable * @return {Object} this. */ restartTour() { return this.startTour(0); } /** * End the current tour. * * @method endTour * @chainable * @return {Object} this. * @fires tool_usertours/tourEnd * @fires tool_usertours/tourEnded */ endTour() { const tourEndEvent = this.dispatchEvent(eventTypes.tourEnd, {}, true); if (tourEndEvent.defaultPrevented) { return this; } if (this.currentStepConfig) { let previousTarget = this.getStepTarget(this.currentStepConfig); if (previousTarget) { if (!previousTarget.attr('tabindex')) { previousTarget.attr('tabindex', '-1'); } previousTarget.focus(); } } this.hide(true); this.tourRunning = false; this.dispatchEvent(eventTypes.tourEnded); return this; } /** * Hide any currently visible steps. * * @method hide * @param {Bool} transition Animate the visibility change * @chainable * @return {Object} this. * @fires tool_usertours/stepHide * @fires tool_usertours/stepHidden */ hide(transition) { const stepHideEvent = this.dispatchEvent(eventTypes.stepHide, {}, true); if (stepHideEvent.defaultPrevented) { return this; } if (this.currentStepNode && this.currentStepNode.length) { this.currentStepNode.hide(); if (this.currentStepPopper) { this.currentStepPopper.destroy(); } } // Restore original target configuration. if (this.currentStepConfig) { let target = this.getStepTarget(this.currentStepConfig); if (target) { if (target.data('original-labelledby')) { target.attr('aria-labelledby', target.data('original-labelledby')); } if (target.data('original-describedby')) { target.attr('aria-describedby', target.data('original-describedby')); } if (target.data('original-tabindex')) { target.attr('tabindex', target.data('tabindex')); } else { // If the target does not have the tabindex attribute at the beginning. We need to remove it. // We should wait a little here before removing the attribute to prevent the browser from adding it again. window.setTimeout(() => { target.removeAttr('tabindex'); }, 400); } } // Clear the step configuration. this.currentStepConfig = null; } let fadeTime = 0; if (transition) { fadeTime = 400; } // Remove the backdrop features. $('[data-flexitour="step-background"]').remove(); $('[data-flexitour="step-backdrop"]').removeAttr('data-flexitour'); $('[data-flexitour="backdrop"]').fadeOut(fadeTime, function() { $(this).remove(); }); // Remove aria-describedby and tabindex attributes. if (this.currentStepNode && this.currentStepNode.length) { let stepId = this.currentStepNode.attr('id'); if (stepId) { let currentStepElement = '[aria-describedby="' + stepId + '-body"]'; $(currentStepElement).removeAttr('tabindex'); $(currentStepElement).removeAttr('aria-describedby'); } } // Reset the listeners. this.resetStepListeners(); this.accessibilityHide(); this.dispatchEvent(eventTypes.stepHidden); this.currentStepNode = null; this.currentStepPopper = null; return this; } /** * Show the current steps. * * @method show * @chainable * @return {Object} this. */ show() { // Show the current step. let startAt = this.getCurrentStepNumber(); return this.gotoStep(startAt); } /** * Return the current step node. * * @method getStepContainer * @return {jQuery} */ getStepContainer() { return $(this.currentStepNode); } /** * Calculate scrollTop. * * @method calculateScrollTop * @param {Object} stepConfig The step configuration of the step * @return {Number} */ calculateScrollTop(stepConfig) { let viewportHeight = $(window).height(); let targetNode = this.getStepTarget(stepConfig); let scrollParent = $(window); if (targetNode.parents('[data-usertour="scroller"]').length) { scrollParent = targetNode.parents('[data-usertour="scroller"]'); } let scrollTop = scrollParent.scrollTop(); if (stepConfig.placement === 'top') { // If the placement is top, center scroll at the top of the target. scrollTop = targetNode.offset().top - (viewportHeight / 2); } else if (stepConfig.placement === 'bottom') { // If the placement is bottom, center scroll at the bottom of the target. scrollTop = targetNode.offset().top + targetNode.height() + scrollTop - (viewportHeight / 2); } else if (targetNode.height() <= (viewportHeight * 0.8)) { // If the placement is left/right, and the target fits in the viewport, centre screen on the target scrollTop = targetNode.offset().top - ((viewportHeight - targetNode.height()) / 2); } else { // If the placement is left/right, and the target is bigger than the viewport, set scrollTop to target.top + buffer // and change step attachmentTarget to top+. scrollTop = targetNode.offset().top - (viewportHeight * 0.2); } // Never scroll over the top. scrollTop = Math.max(0, scrollTop); // Never scroll beyond the bottom. scrollTop = Math.min($(document).height() - viewportHeight, scrollTop); return Math.ceil(scrollTop); } /** * Calculate dialogue position for page middle. * * @param {jQuery} currentStepNode Current step node * @method calculateScrollTop */ calculateStepPositionInPage(currentStepNode) { let top = MINSPACING; const viewportHeight = $(window).height(); const stepHeight = currentStepNode.height(); const viewportWidth = $(window).width(); const stepWidth = currentStepNode.width(); if (viewportHeight >= (stepHeight + (MINSPACING * 2))) { top = Math.ceil((viewportHeight - stepHeight) / 2); } else { const headerHeight = currentStepNode.find('.modal-header').first().outerHeight() ?? 0; const footerHeight = currentStepNode.find('.modal-footer').first().outerHeight() ?? 0; const currentStepBody = currentStepNode.find('[data-placeholder="body"]').first(); const maxHeight = viewportHeight - (MINSPACING * 2) - headerHeight - footerHeight; currentStepBody.css({ 'max-height': maxHeight + 'px', 'overflow': 'auto', }); } currentStepNode.offset({ top: top, left: Math.ceil((viewportWidth - stepWidth) / 2) }); } /** * Position the step on the page. * * @method positionStep * @param {Object} stepConfig The step configuration of the step * @chainable * @return {Object} this. */ positionStep(stepConfig) { let content = this.currentStepNode; let thisT = this; if (!content || !content.length) { // Unable to find the step node. return this; } stepConfig.placement = this.recalculatePlacement(stepConfig); let flipBehavior; switch (stepConfig.placement) { case 'left': flipBehavior = ['left', 'right', 'top', 'bottom']; break; case 'right': flipBehavior = ['right', 'left', 'top', 'bottom']; break; case 'top': flipBehavior = ['top', 'bottom', 'right', 'left']; break; case 'bottom': flipBehavior = ['bottom', 'top', 'right', 'left']; break; default: flipBehavior = 'flip'; break; } let target = this.getStepTarget(stepConfig); var config = { placement: stepConfig.placement + '-start', removeOnDestroy: true, modifiers: { flip: { behaviour: flipBehavior, }, arrow: { element: '[data-role="arrow"]', }, }, onCreate: function(data) { recalculateArrowPosition(data); recalculateStepPosition(data); }, onUpdate: function(data) { recalculateArrowPosition(data); if (thisT.possitionNeedToBeRecalculated) { thisT.recalculatedNo++; thisT.possitionNeedToBeRecalculated = false; recalculateStepPosition(data); } }, }; let recalculateArrowPosition = function(data) { let placement = data.placement.split('-')[0]; const isVertical = ['left', 'right'].indexOf(placement) !== -1; const arrowElement = data.instance.popper.querySelector('[data-role="arrow"]'); const stepElement = $(data.instance.popper.querySelector('[data-role="flexitour-step"]')); if (isVertical) { let arrowHeight = parseFloat(window.getComputedStyle(arrowElement).height); let arrowOffset = parseFloat(window.getComputedStyle(arrowElement).top); let popperHeight = parseFloat(window.getComputedStyle(data.instance.popper).height); let popperOffset = parseFloat(window.getComputedStyle(data.instance.popper).top); let popperBorderWidth = parseFloat(stepElement.css('borderTopWidth')); let popperBorderRadiusWidth = parseFloat(stepElement.css('borderTopLeftRadius')) * 2; let arrowPos = arrowOffset + (arrowHeight / 2); let maxPos = popperHeight + popperOffset - popperBorderWidth - popperBorderRadiusWidth; let minPos = popperOffset + popperBorderWidth + popperBorderRadiusWidth; if (arrowPos >= maxPos || arrowPos <= minPos) { let newArrowPos = 0; if (arrowPos > (popperHeight / 2)) { newArrowPos = maxPos - arrowHeight; } else { newArrowPos = minPos + arrowHeight; } $(arrowElement).css('top', newArrowPos); } } else { let arrowWidth = parseFloat(window.getComputedStyle(arrowElement).width); let arrowOffset = parseFloat(window.getComputedStyle(arrowElement).left); let popperWidth = parseFloat(window.getComputedStyle(data.instance.popper).width); let popperOffset = parseFloat(window.getComputedStyle(data.instance.popper).left); let popperBorderWidth = parseFloat(stepElement.css('borderTopWidth')); let popperBorderRadiusWidth = parseFloat(stepElement.css('borderTopLeftRadius')) * 2; let arrowPos = arrowOffset + (arrowWidth / 2); let maxPos = popperWidth + popperOffset - popperBorderWidth - popperBorderRadiusWidth; let minPos = popperOffset + popperBorderWidth + popperBorderRadiusWidth; if (arrowPos >= maxPos || arrowPos <= minPos) { let newArrowPos = 0; if (arrowPos > (popperWidth / 2)) { newArrowPos = maxPos - arrowWidth; } else { newArrowPos = minPos + arrowWidth; } $(arrowElement).css('left', newArrowPos); } } }; const recalculateStepPosition = function(data) { const placement = data.placement.split('-')[0]; const isVertical = ['left', 'right'].indexOf(placement) !== -1; const popperElement = $(data.instance.popper); const targetElement = $(data.instance.reference); const arrowElement = popperElement.find('[data-role="arrow"]'); const stepElement = popperElement.find('[data-role="flexitour-step"]'); const viewportHeight = $(window).height(); const viewportWidth = $(window).width(); const arrowHeight = parseFloat(arrowElement.outerHeight(true)); const popperHeight = parseFloat(popperElement.outerHeight(true)); const targetHeight = parseFloat(targetElement.outerHeight(true)); const arrowWidth = parseFloat(arrowElement.outerWidth(true)); const popperWidth = parseFloat(popperElement.outerWidth(true)); const targetWidth = parseFloat(targetElement.outerWidth(true)); let maxHeight; if (thisT.recalculatedNo > 1) { // The current screen is too small, and cannot fit with the original placement. // We should set the placement to auto so the PopperJS can calculate the perfect placement. thisT.currentStepPopper.options.placement = isVertical ? 'auto-left' : 'auto-bottom'; } if (thisT.recalculatedNo > 2) { // Return here to prevent recursive calling. return; } if (isVertical) { // Find the best place to put the tour: Left of right. const leftSpace = targetElement.offset().left > 0 ? targetElement.offset().left : 0; const rightSpace = viewportWidth - leftSpace - targetWidth; const remainingSpace = leftSpace >= rightSpace ? leftSpace : rightSpace; maxHeight = viewportHeight - MINSPACING * 2; if (remainingSpace < (popperWidth + arrowWidth)) { const maxWidth = remainingSpace - MINSPACING - arrowWidth; if (maxWidth > 0) { popperElement.css({ 'max-width': maxWidth + 'px', }); // Not enough space, flag true to make Popper to recalculate the position. thisT.possitionNeedToBeRecalculated = true; } } else if (maxHeight < popperHeight) { // Check if the Popper's height can fit the viewport height or not. // If not, set the correct max-height value for the Popper element. popperElement.css({ 'max-height': maxHeight + 'px', }); } } else { // Find the best place to put the tour: Top of bottom. const topSpace = targetElement.offset().top > 0 ? targetElement.offset().top : 0; const bottomSpace = viewportHeight - topSpace - targetHeight; const remainingSpace = topSpace >= bottomSpace ? topSpace : bottomSpace; maxHeight = remainingSpace - MINSPACING - arrowHeight; if (remainingSpace < (popperHeight + arrowHeight)) { // Not enough space, flag true to make Popper to recalculate the position. thisT.possitionNeedToBeRecalculated = true; } } // Check if the Popper's height can fit the viewport height or not. // If not, set the correct max-height value for the body. const currentStepBody = stepElement.find('[data-placeholder="body"]').first(); const headerEle = stepElement.find('.modal-header').first(); const footerEle = stepElement.find('.modal-footer').first(); const headerHeight = headerEle.outerHeight(true) ?? 0; const footerHeight = footerEle.outerHeight(true) ?? 0; maxHeight = maxHeight - headerHeight - footerHeight; if (maxHeight > 0) { headerEle.removeClass('minimal'); footerEle.removeClass('minimal'); currentStepBody.css({ 'max-height': maxHeight + 'px', 'overflow': 'auto', }); } else { headerEle.addClass('minimal'); footerEle.addClass('minimal'); } // Call the Popper update method to update the position. thisT.currentStepPopper.update(); }; let background = $('[data-flexitour="step-background"]'); if (background.length) { target = background; } this.currentStepPopper = new Popper(target, content[0], config); return this; } /** * For left/right placement, checks that there is room for the step at current window size. * * If there is not enough room, changes placement to 'top'. * * @method recalculatePlacement * @param {Object} stepConfig The step configuration of the step * @return {String} The placement after recalculate */ recalculatePlacement(stepConfig) { const buffer = 10; const arrowWidth = 16; let target = this.getStepTarget(stepConfig); let widthContent = this.currentStepNode.width() + arrowWidth; let targetOffsetLeft = target.offset().left - buffer; let targetOffsetRight = target.offset().left + target.width() + buffer; let placement = stepConfig.placement; if (['left', 'right'].indexOf(placement) !== -1) { if ((targetOffsetLeft < (widthContent + buffer)) && ((targetOffsetRight + widthContent + buffer) > document.documentElement.clientWidth)) { placement = 'top'; } } return placement; } /** * Add the backdrop. * * @method positionBackdrop * @param {Object} stepConfig The step configuration of the step * @chainable * @return {Object} this. */ positionBackdrop(stepConfig) { if (stepConfig.backdrop) { this.currentStepConfig.hasBackdrop = true; let backdrop = $('<div data-flexitour="backdrop"></div>'); if (stepConfig.zIndex) { if (stepConfig.attachPoint === 'append') { stepConfig.attachTo.append(backdrop); } else { backdrop.insertAfter(stepConfig.attachTo); } } else { $('body').append(backdrop); } if (this.isStepActuallyVisible(stepConfig)) { // The step has a visible target. // Punch a hole through the backdrop. let background = $('[data-flexitour="step-background"]'); if (!background.length) { background = $('<div data-flexitour="step-background"></div>'); } let targetNode = this.getStepTarget(stepConfig); let buffer = 10; let colorNode = targetNode; if (buffer) { colorNode = $('body'); } let drawertop = 0; if (targetNode.parents('[data-usertour="scroller"]').length) { const scrollerElement = targetNode.parents('[data-usertour="scroller"]'); const navigationBuffer = scrollerElement.offset().top; if (scrollerElement.scrollTop() >= navigationBuffer) { drawertop = scrollerElement.scrollTop() - navigationBuffer; background.css({ position: 'fixed' }); } } background.css({ width: targetNode.outerWidth() + buffer + buffer, height: targetNode.outerHeight() + buffer + buffer, left: targetNode.offset().left - buffer, top: targetNode.offset().top + drawertop - buffer, backgroundColor: this.calculateInherittedBackgroundColor(colorNode), }); if (targetNode.offset().left < buffer) { background.css({ width: targetNode.outerWidth() + targetNode.offset().left + buffer, left: targetNode.offset().left, }); } if ((targetNode.offset().top + drawertop) < buffer) { background.css({ height: targetNode.outerHeight() + targetNode.offset().top + buffer, top: targetNode.offset().top, }); } let targetRadius = targetNode.css('borderRadius'); if (targetRadius && targetRadius !== $('body').css('borderRadius')) { background.css('borderRadius', targetRadius); } let targetPosition = this.calculatePosition(targetNode); if (targetPosition === 'absolute') { background.css('position', 'fixed'); } let fader = background.clone(); fader.css({ backgroundColor: backdrop.css('backgroundColor'), opacity: backdrop.css('opacity'), }); fader.attr('data-flexitour', 'step-background-fader'); if (targetNode.parents('[data-region="fixed-drawer"]').length) { let targetClone = targetNode.clone(); background.append(targetClone); } if (stepConfig.zIndex) { if (stepConfig.attachPoint === 'append') { stepConfig.attachTo.append(background); } else { fader.insertAfter(stepConfig.attachTo); background.insertAfter(stepConfig.attachTo); } } else { $('body').append(fader); $('body').append(background); } // Add the backdrop data to the actual target. // This is the part which actually does the work. targetNode.attr('data-flexitour', 'step-backdrop'); if (stepConfig.zIndex) { backdrop.css('zIndex', stepConfig.zIndex); background.css('zIndex', stepConfig.zIndex + 1); targetNode.css('zIndex', stepConfig.zIndex + 2); } fader.fadeOut('2000', function() { $(this).remove(); }); } } return this; } /** * Calculate the inheritted z-index. * * @method calculateZIndex * @param {jQuery} elem The element to calculate z-index for * @return {Number} Calculated z-index */ calculateZIndex(elem) { elem = $(elem); while (elem.length && elem[0] !== document) { // Ignore z-index if position is set to a value where z-index is ignored by the browser // This makes behavior of this function consistent across browsers // WebKit always returns auto if the element is positioned. let position = elem.css("position"); if (position === "absolute" || position === "relative" || position === "fixed") { // IE returns 0 when zIndex is not specified // other browsers return a string // we ignore the case of nested elements with an explicit value of 0 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div> let value = parseInt(elem.css("zIndex"), 10); if (!isNaN(value) && value !== 0) { return value; } } elem = elem.parent(); } return 0; } /** * Calculate the inheritted background colour. * * @method calculateInherittedBackgroundColor * @param {jQuery} elem The element to calculate colour for * @return {String} Calculated background colour */ calculateInherittedBackgroundColor(elem) { // Use a fake node to compare each element against. let fakeNode = $('<div>').hide(); $('body').append(fakeNode); let fakeElemColor = fakeNode.css('backgroundColor'); fakeNode.remove(); elem = $(elem); while (elem.length && elem[0] !== document) { let color = elem.css('backgroundColor'); if (color !== fakeElemColor) { return color; } elem = elem.parent(); } return null; } /** * Calculate the inheritted position. * * @method calculatePosition * @param {jQuery} elem The element to calculate position for * @return {String} Calculated position */ calculatePosition(elem) { elem = $(elem); while (elem.length && elem[0] !== document) { let position = elem.css('position'); if (position !== 'static') { return position; } elem = elem.parent(); } return null; } /** * Perform accessibility changes for step shown. * * This will add aria-hidden="true" to all siblings and parent siblings. * * @method accessibilityShow */ accessibilityShow() { let stateHolder = 'data-has-hidden'; let attrName = 'aria-hidden'; let hideFunction = function(child) { let flexitourRole = child.data('flexitour'); if (flexitourRole) { switch (flexitourRole) { case 'container': case 'target': return; } } let hidden = child.attr(attrName); if (!hidden) { child.attr(stateHolder, true); Aria.hide(child); } }; this.currentStepNode.siblings().each(function(index, node) { hideFunction($(node)); }); this.currentStepNode.parentsUntil('body').siblings().each(function(index, node) { hideFunction($(node)); }); } /** * Perform accessibility changes for step hidden. * * This will remove any newly added aria-hidden="true". * * @method accessibilityHide */ accessibilityHide() { let stateHolder = 'data-has-hidden'; let showFunction = function(child) { let hidden = child.attr(stateHolder); if (typeof hidden !== 'undefined') { child.removeAttr(stateHolder); Aria.unhide(child); } }; $('[' + stateHolder + ']').each(function(index, node) { showFunction($(node)); }); } }; export default Tour; src/managetours.js 0000644 00000002530 15152263320 0010215 0 ustar 00 /** * Tour management code. * * @module tool_usertours/managetours * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk> */ import {prefetchStrings} from 'core/prefetch'; import {get_string as getString} from 'core/str'; import {confirm as confirmModal} from 'core/notification'; /** * Handle tour management actions. * * @param {Event} e * @private */ const removeTourHandler = e => { const deleteButton = e.target.closest('[data-action="delete"]'); if (deleteButton) { e.preventDefault(); removeTourFromLink(deleteButton.href); } }; /** * Handle removal of a tour with confirmation. * * @param {string} targetUrl * @private */ const removeTourFromLink = targetUrl => { confirmModal( getString('confirmtourremovaltitle', 'tool_usertours'), getString('confirmtourremovalquestion', 'tool_usertours'), getString('yes', 'core'), getString('no', 'core'), () => { window.location = targetUrl; } ); }; /** * Set up the tour management handlers. */ export const setup = () => { prefetchStrings('tool_usertours', [ 'confirmtourremovaltitle', 'confirmtourremovalquestion', ]); prefetchStrings('core', [ 'yes', 'no', ]); document.querySelector('body').addEventListener('click', removeTourHandler); }; src/managesteps.js 0000644 00000002530 15152263320 0010177 0 ustar 00 /** * Step management code. * * @module tool_usertours/managesteps * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk> */ import {prefetchStrings} from 'core/prefetch'; import {get_string as getString} from 'core/str'; import {confirm as confirmModal} from 'core/notification'; /** * Handle step management actions. * * @param {Event} e * @private */ const removeStepHandler = e => { const deleteButton = e.target.closest('[data-action="delete"]'); if (deleteButton) { e.preventDefault(); removeStepFromLink(deleteButton.href); } }; /** * Handle removal of a step with confirmation. * * @param {string} targetUrl * @private */ const removeStepFromLink = targetUrl => { confirmModal( getString('confirmstepremovaltitle', 'tool_usertours'), getString('confirmstepremovalquestion', 'tool_usertours'), getString('yes', 'core'), getString('no', 'core'), () => { window.location = targetUrl; } ); }; /** * Set up the step management handlers. */ export const setup = () => { prefetchStrings('tool_usertours', [ 'confirmstepremovaltitle', 'confirmstepremovalquestion', ]); prefetchStrings('core', [ 'yes', 'no', ]); document.querySelector('body').addEventListener('click', removeStepHandler); }; src/filter_cssselector.js 0000644 00000002464 15152263320 0011574 0 ustar 00 // This file is part of Moodle - http://moodle.org/ // // Moodle is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Moodle is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Moodle. If not, see <http://www.gnu.org/licenses/>. /** * CSS selector client side filter. * * @module tool_usertours/filter_cssselector * @copyright 2020 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ /** * Checks whether the configured CSS selector exists on this page. * * @param {array} tourConfig The tour configuration. * @returns {boolean} */ export const filterMatches = function(tourConfig) { let filterValues = tourConfig.filtervalues.cssselector; if (filterValues[0]) { return !!document.querySelector(filterValues[0]); } // If there is no CSS selector configured, this page matches. return true; };
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | ���֧ߧ֧�ѧ�ڧ� ����ѧߧڧ��: 0.29 |
proxy
|
phpinfo
|
���ѧ����ۧܧ�