diff --git a/Gemfile b/Gemfile
index 5a0ee6553f..0e970ea39a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -161,7 +161,7 @@ gem 'sprockets', '~> 3.7.0'
# also, better than thin since we can control worker concurrency.
gem 'unicorn'
-gem 'nokogiri', '~> 1.8.4'
+gem 'nokogiri', '~> 1.8.5'
# carrierwave 0.11.3 should allow to use fog-aws without the rest of the
# fog dependency chain. We only need aws here, so we can avoid it
diff --git a/Gemfile.lock b/Gemfile.lock
index e981f1c1d9..6bd367da5e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -86,14 +86,14 @@ GIT
PATH
remote: vendored-plugins/openproject-auth_plugins
specs:
- openproject-auth_plugins (8.0.2)
+ openproject-auth_plugins (8.1.0)
omniauth (~> 1.0)
rails (~> 5.0)
PATH
remote: vendored-plugins/openproject-avatars
specs:
- openproject-avatars (8.0.2)
+ openproject-avatars (8.1.0)
fastimage (~> 2.1.0)
gravatar_image_tag (~> 1.2.0)
rails (~> 5.0)
@@ -101,52 +101,52 @@ PATH
PATH
remote: vendored-plugins/openproject-backlogs
specs:
- openproject-backlogs (8.0.2)
+ openproject-backlogs (8.1.0)
acts_as_silent_list (~> 3.0.0)
- openproject-pdf_export (= 8.0.2)
+ openproject-pdf_export (= 8.1.0)
PATH
remote: vendored-plugins/openproject-costs
specs:
- openproject-costs (8.0.2)
+ openproject-costs (8.1.0)
PATH
remote: vendored-plugins/openproject-documents
specs:
- openproject-documents (8.0.2)
+ openproject-documents (8.1.0)
PATH
remote: vendored-plugins/openproject-github_integration
specs:
- openproject-github_integration (8.0.2)
- openproject-webhooks (~> 8.0.2)
+ openproject-github_integration (8.1.0)
+ openproject-webhooks (~> 8.1.0)
rails (~> 5.0)
PATH
remote: vendored-plugins/openproject-global_roles
specs:
- openproject-global_roles (8.0.2)
+ openproject-global_roles (8.1.0)
PATH
remote: vendored-plugins/openproject-ldap_groups
specs:
- openproject-ldap_groups (8.0.2)
+ openproject-ldap_groups (8.1.0)
PATH
remote: vendored-plugins/openproject-meeting
specs:
- openproject-meeting (8.0.2)
+ openproject-meeting (8.1.0)
icalendar (~> 2.3.0)
PATH
remote: vendored-plugins/openproject-my_project_page
specs:
- openproject-my_project_page (8.0.2)
+ openproject-my_project_page (8.1.0)
PATH
remote: vendored-plugins/openproject-openid_connect
specs:
- openproject-openid_connect (8.0.2)
+ openproject-openid_connect (8.1.0)
lobby_boy (~> 0.1.3)
omniauth-openid_connect-providers (~> 0.1)
openproject-auth_plugins (~> 8.0)
@@ -155,22 +155,22 @@ PATH
PATH
remote: vendored-plugins/openproject-pdf_export
specs:
- openproject-pdf_export (8.0.2)
+ openproject-pdf_export (8.1.0)
pdf-inspector (~> 1.3.0)
prawn (~> 2.2)
PATH
remote: vendored-plugins/openproject-reporting
specs:
- openproject-reporting (8.0.2)
+ openproject-reporting (8.1.0)
jquery-tablesorter (~> 1.25.5)
- openproject-costs (= 8.0.2)
+ openproject-costs (= 8.1.0)
reporting_engine (>= 1.1.0)
PATH
remote: vendored-plugins/openproject-two_factor_authentication
specs:
- openproject-two_factor_authentication (8.0.2)
+ openproject-two_factor_authentication (8.1.0)
aws-sdk-sns (~> 1.1.0)
messagebird-rest (~> 1.3.2)
rails (~> 5)
@@ -179,19 +179,19 @@ PATH
PATH
remote: vendored-plugins/openproject-webhooks
specs:
- openproject-webhooks (8.0.2)
+ openproject-webhooks (8.1.0)
rails (~> 5.0)
PATH
remote: vendored-plugins/openproject-xls_export
specs:
- openproject-xls_export (8.0.2)
+ openproject-xls_export (8.1.0)
spreadsheet (~> 0.8.9)
PATH
remote: vendored-plugins/reporting_engine
specs:
- reporting_engine (8.0.2)
+ reporting_engine (8.1.0)
json
rails (~> 5.1.0)
@@ -524,7 +524,7 @@ GEM
netrc (0.11.0)
newrelic_rpm (4.5.0.337)
nio4r (2.3.0)
- nokogiri (1.8.4)
+ nokogiri (1.8.5)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
@@ -880,7 +880,7 @@ DEPENDENCIES
mysql2 (~> 0.5.0)
net-ldap (~> 0.16.0)
newrelic_rpm
- nokogiri (~> 1.8.4)
+ nokogiri (~> 1.8.5)
oj (~> 3.5.0)
okcomputer (~> 1.16.0)
omniauth!
diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb
index 0aba70d0cd..6f204eccac 100644
--- a/app/assets/javascripts/application.js.erb
+++ b/app/assets/javascripts/application.js.erb
@@ -49,6 +49,9 @@
//= require danger_zone_validation
//= require flash_messages
+//= require_tree ./onboarding
+
+
function checkAll(selector, checked) {
jQuery('#' + selector + ' input:checkbox').not(':disabled').each(function() {
this.checked = checked;
@@ -241,20 +244,6 @@ jQuery(document).ready(function($) {
addClickEventToAllErrorMessages();
});
- // file table thumbnails
- jQuery("table a.has-thumb").hover(function() {
- jQuery(this).removeAttr("title").toggleClass("active");
-
- // grab the image dimensions to position it properly
- var thumbImg = $(this).find("img");
- var thumbImgLeft = -(thumbImg.outerWidth() );
- var thumbImgTop = -(thumbImg.height() / 2 );
- thumbImg.css({top: thumbImgTop, left: thumbImgLeft}).show();
-
- }, function() {
- jQuery(this).toggleClass("active").find("img").hide();
- });
-
// show/hide the files table
jQuery(".attachments h4").click(function() {
jQuery(this).toggleClass("closed").next().slideToggle(animationRate);
diff --git a/app/assets/javascripts/members_form.js b/app/assets/javascripts/members_form.js
index 8e99077afb..5f7d844223 100644
--- a/app/assets/javascripts/members_form.js
+++ b/app/assets/javascripts/members_form.js
@@ -67,13 +67,6 @@ function hideAddMemberForm() {
}
jQuery(document).ready(function($) {
- $("#tab-content-members").submit('#members_add_form', function () {
- var error = $('.errorExplanation, .flash');
- if (error) {
- error.remove();
- }
- });
-
// Show/Hide content when page is loaded
if (window.OpenProject.guardedLocalStorage("showFilter") === "true") {
showFilter(filter = findFilter());
diff --git a/app/assets/javascripts/onboarding/backlogs_tour.js b/app/assets/javascripts/onboarding/backlogs_tour.js
new file mode 100644
index 0000000000..6690a6abe4
--- /dev/null
+++ b/app/assets/javascripts/onboarding/backlogs_tour.js
@@ -0,0 +1,45 @@
+(function ($) {
+ $(function() {
+ window.scrumBacklogsTourSteps = [
+ {
+ 'next #content-wrapper': I18n.t('js.onboarding.steps.backlogs_overview'),
+ 'showSkip': false,
+ 'containerClass': '-dark -hidden-arrow'
+ },
+ {
+ 'event_type': 'next',
+ 'selector': '#sprint_backlogs_container .backlog .menu-trigger',
+ 'description': I18n.t('js.onboarding.steps.backlogs_task_board_arrow'),
+ 'showSkip': false,
+ onNext: function () {
+ $('#sprint_backlogs_container .backlog .menu-trigger')[0].click();
+ }
+ },
+ {
+ 'event_type': 'next',
+ 'selector': '#sprint_backlogs_container .backlog .menu .items',
+ 'description': I18n.t('js.onboarding.steps.backlogs_task_board_select'),
+ 'showSkip': false,
+ 'containerClass': '-dark',
+ onNext: function () {
+ $('#sprint_backlogs_container .backlog .show_task_board')[0].click();
+ }
+ }
+ ];
+
+ window.scrumTaskBoardTourSteps = [
+ {
+ 'next #content-wrapper': I18n.t('js.onboarding.steps.backlogs_task_board'),
+ 'showSkip': false,
+ 'containerClass': '-dark -hidden-arrow'
+ },
+ {
+ 'next #main-menu-work-packages-wrapper': I18n.t('js.onboarding.steps.wp_toggler'),
+ 'showSkip': false,
+ onNext: function () {
+ $('#main-menu-work-packages')[0].click();
+ }
+ },
+ ];
+ });
+}(jQuery))
diff --git a/app/assets/javascripts/onboarding/homescreen_tour.js b/app/assets/javascripts/onboarding/homescreen_tour.js
new file mode 100644
index 0000000000..00686a4935
--- /dev/null
+++ b/app/assets/javascripts/onboarding/homescreen_tour.js
@@ -0,0 +1,34 @@
+(function ($) {
+ $(function() {
+ window.homescreenOnboardingTourSteps = [
+ {
+ 'next #top-menu': I18n.t('js.onboarding.steps.welcome'),
+ 'skipButton': {className: 'enjoyhint_btn-transparent'},
+ 'containerClass': '-hidden-arrow'
+ },
+ {
+ 'description': I18n.t('js.onboarding.steps.project_selection'),
+ 'selector': '.widget-box.welcome',
+ 'event': 'custom',
+ 'showSkip': false,
+ 'containerClass': '-dark -hidden-arrow',
+ 'containerClass': '-dark -hidden-arrow',
+ 'clickable': true,
+ onBeforeStart: function () {
+ // Handle the correct project selection and redirection
+ // This will be removed once the project selection is implemented
+ jQuery(".widget-box.welcome a:contains(" + scrumDemoProjectName + ")").click(function () {
+ tutorialInstance.trigger('next');
+ window.location = this.href + '/backlogs/?start_scrum_onboarding_tour=true';
+ });
+ jQuery(".widget-box.welcome a:contains(" + demoProjectName + ")").click(function () {
+ tutorialInstance.trigger('next');
+ window.location = this.href + '/work_packages/?start_onboarding_tour=true';
+ });
+ // Disable clicks on other links
+ $('.widget-box.welcome a').addClass('-disabled').bind('click', preventClickHandler);
+ }
+ }
+ ];
+ });
+}(jQuery))
diff --git a/app/assets/javascripts/onboarding/onboarding_tour.js b/app/assets/javascripts/onboarding/onboarding_tour.js
new file mode 100644
index 0000000000..eb37d90d24
--- /dev/null
+++ b/app/assets/javascripts/onboarding/onboarding_tour.js
@@ -0,0 +1,125 @@
+(function ($) {
+ $(function() {
+ // ------------------------------- Global -------------------------------
+ window.tutorialInstance;
+ window.preventClickHandler = function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ };
+ window.waitForElement = function(element, container, execFunction) {
+ // Wait for the element to be ready
+ var observer = new MutationObserver(function (mutations, observerInstance) {
+ if ($(element).length) {
+ observerInstance.disconnect(); // stop observing
+ execFunction();
+ return;
+ }
+ });
+ observer.observe($(container)[0], {
+ childList: true,
+ subtree: true
+ });
+ };
+ window.demoProjectName = 'Demo project';
+ window.scrumDemoProjectName = 'Scrum project';
+
+ var storageKey = 'openProject-onboardingTour';
+ var currentTourPart = sessionStorage.getItem(storageKey);
+ var url = new URL(window.location.href);
+
+ // ------------------------------- Initial start -------------------------------
+ // Do not show the tutorial on mobile or when the demo data has been deleted
+ if(!(bowser.mobile || bowser.ios || bowser.android) && $('meta[name=demo_projects_available]').attr('content') == "true") {
+
+ // Start after the intro modal (language selection)
+ // This has to be changed once the project selection is implemented
+ if (url.searchParams.get("first_time_user") && demoProjectsLinks().length == 2) {
+ currentTourPart = '';
+ sessionStorage.setItem(storageKey, 'readyToStart');
+
+ // Start automatically when the language selection is closed
+ $('.op-modal--modal-close-button').click(function () {
+ homescreenTour();
+ });
+ }
+
+ // ------------------------------- Tutorial Homescreen page -------------------------------
+ if (currentTourPart === "readyToStart") {
+ homescreenTour();
+ }
+
+ // ------------------------------- Tutorial WP page -------------------------------
+ if (currentTourPart === "startWpTour" || url.searchParams.get("start_onboarding_tour")) {
+ workPackageTour();
+ }
+
+ // ------------------------------- Tutorial Backlogs page -------------------------------
+ if (url.searchParams.get("start_scrum_onboarding_tour")) {
+ if ($('.backlogs-menu-item').length > 0) {
+ backlogsTour();
+ }
+ }
+
+ // ------------------------------- Tutorial Task Board page -------------------------------
+ if (currentTourPart === "startTaskBoardTour") {
+ taskboardTour();
+ }
+ }
+
+ function demoProjectsLinks() {
+ demoProjects = [];
+ demoProjectsLink = jQuery(".widget-box.welcome a:contains(" + demoProjectName + ")");
+ scrumDemoProjectsLink = jQuery(".widget-box.welcome a:contains(" + scrumDemoProjectName + ")");
+ if (demoProjectsLink.length) demoProjects.push(demoProjectsLink);
+ if (scrumDemoProjectsLink.length) demoProjects.push(scrumDemoProjectsLink);
+
+ return demoProjects;
+ }
+
+ function initializeTour(storageValue, disabledElements, projectSelection) {
+ tutorialInstance = new EnjoyHint({
+ onStart: function () {
+ $('#content-wrapper, #menu-sidebar').addClass('-hidden-overflow');
+ },
+ onEnd: function () {
+ sessionStorage.setItem(storageKey, storageValue);
+ $('#content-wrapper, #menu-sidebar').removeClass('-hidden-overflow');
+ },
+ onSkip: function () {
+ sessionStorage.setItem(storageKey, 'skipped');
+ if (disabledElements) jQuery(disabledElements).removeClass('-disabled').unbind('click', preventClickHandler);
+ if (projectSelection) $.each(demoProjectsLinks(), function(i, e) { $(e).off('click')});
+ $('#content-wrapper, #menu-sidebar').removeClass('-hidden-overflow');
+ }
+ });
+ }
+
+ function startTour(steps) {
+ tutorialInstance.set(steps);
+ tutorialInstance.run();
+ }
+
+ function homescreenTour() {
+ initializeTour('startProjectTour', '.widget-box--blocks--buttons a', true);
+ startTour(homescreenOnboardingTourSteps);
+ }
+
+ function backlogsTour() {
+ initializeTour('startTaskBoardTour');
+ startTour(scrumBacklogsTourSteps);
+ }
+
+ function taskboardTour() {
+ initializeTour('startWpTour');
+ startTour(scrumTaskBoardTourSteps);
+ }
+
+ function workPackageTour() {
+ initializeTour('wpFinished');
+
+ waitForElement('.work-package--results-tbody', '.work-packages-split-view--tabletimeline-side', function() {
+ startTour(wpOnboardingTourSteps);
+ });
+ }
+ });
+}(jQuery));
diff --git a/app/assets/javascripts/onboarding/work_package_tour.js b/app/assets/javascripts/onboarding/work_package_tour.js
new file mode 100644
index 0000000000..05395c51bb
--- /dev/null
+++ b/app/assets/javascripts/onboarding/work_package_tour.js
@@ -0,0 +1,64 @@
+(function ($) {
+ $(function() {
+ window.wpOnboardingTourSteps = [
+ {
+ 'next .wp-table--row': I18n.t('js.onboarding.steps.wp_list'),
+ 'showSkip': false,
+ onNext: function () {
+ $(".wp-table--cell-span.id a ")[0].click();
+ }
+ },
+ {
+ 'next .work-packages-full-view--split-left': I18n.t('js.onboarding.steps.wp_full_view'),
+ 'showSkip': false,
+ 'containerClass': '-dark -hidden-arrow'
+ },
+ {
+ 'next .work-packages-list-view-button': I18n.t('js.onboarding.steps.wp_back_button'),
+ 'showSkip': false,
+ onNext: function () {
+ $('.work-packages-list-view-button')[0].click();
+ }
+ },
+ {
+ 'next .add-work-package': I18n.t('js.onboarding.steps.wp_create_button'),
+ 'showSkip': false,
+ 'shape': 'circle'
+ },
+ {
+ 'next .timeline-toolbar--button': I18n.t('js.onboarding.steps.wp_timeline_button'),
+ 'showSkip': false,
+ 'shape': 'circle',
+ onNext: function () {
+ $('.timeline-toolbar--button')[0].click();
+ }
+ },
+ {
+ 'next .work-packages-tabletimeline--timeline-side': I18n.t('js.onboarding.steps.wp_timeline'),
+ 'showSkip': false,
+ 'containerClass': '-dark -hidden-arrow'
+ },
+ {
+ 'next .main-menu--arrow-left-to-project': I18n.t('js.onboarding.steps.sidebar_arrow'),
+ 'showSkip': false,
+ onNext: function () {
+ $('.main-menu--arrow-left-to-project')[0].click();
+ }
+ },
+ {
+ 'next .members-menu-item': I18n.t('js.onboarding.steps.members'),
+ 'showSkip': false
+ },
+ {
+ 'next .wiki-menu--main-item': I18n.t('js.onboarding.steps.wiki'),
+ 'showSkip': false
+ },
+ {
+ 'next .menu-item--help': I18n.t('js.onboarding.steps.help_menu'),
+ 'shape': 'circle',
+ 'nextButton': {text: I18n.t('js.onboarding.steps.got_it')},
+ 'showSkip': false
+ }
+ ];
+ });
+}(jQuery))
diff --git a/app/assets/javascripts/settings.js.erb b/app/assets/javascripts/settings.js.erb
index 82a21f54a2..aa81c73aab 100644
--- a/app/assets/javascripts/settings.js.erb
+++ b/app/assets/javascripts/settings.js.erb
@@ -100,5 +100,26 @@ See docs/COPYRIGHT.rdoc for more details.
.removeAttr('required') // Rails 4.0 still seems to use attribute
.prop('required', wasChecked);
})
+
+ /** Toggle highlighted attributes visibility depending on if the highlighting mode 'inline' was selected*/
+ $('.settings--highlighting-mode select').change(function() {
+ var highlightingMode = $(this).val();
+ $(".settings--highlighted-attributes").toggle(highlightingMode === "inline")
+ })
+
+ /** Initialize hightlighted attributes checkboxes. If none is selected, it means we want them all. So let's
+ * show them all as selected.
+ * On submitting the form, we remove all checkboxes before sending to communicate, we actually want all and not
+ * only the selected.*/
+ if ($(".settings--highlighted-attributes input[type='checkbox']:checked").length == 0) {
+ $(".settings--highlighted-attributes input[type='checkbox']").prop("checked", true);
+ }
+ $('#tab-content-work_packages form').submit(function() {
+ var availableAttributes = $(".settings--highlighted-attributes input[type='checkbox']");
+ var selectedAttributes = $(".settings--highlighted-attributes input[type='checkbox']:checked");
+ if (selectedAttributes.length == availableAttributes.length) {
+ availableAttributes.prop("checked", false);
+ }
+ })
});
}(jQuery));
diff --git a/app/assets/javascripts/specific/main_menu.js.erb b/app/assets/javascripts/specific/main_menu.js.erb
index 3d5c97f552..458c9787ec 100644
--- a/app/assets/javascripts/specific/main_menu.js.erb
+++ b/app/assets/javascripts/specific/main_menu.js.erb
@@ -115,4 +115,8 @@ jQuery(document).ready(function($) {
$(child).before(header);
})
+ if($('.menu_root').hasClass('closed')) {
+ // TODO: Instead of hiding the sidebar move sidebar's contents to submenus and cache it.
+ $('#sidebar').toggleClass('-hidden', true);
+ }
});
diff --git a/app/assets/javascripts/vendor/enjoyhint.js b/app/assets/javascripts/vendor/enjoyhint.js
new file mode 100755
index 0000000000..4de8647ddc
--- /dev/null
+++ b/app/assets/javascripts/vendor/enjoyhint.js
@@ -0,0 +1,1589 @@
+var EnjoyHint;
+
+(function ($) {
+ EnjoyHint = function (_options) {
+ var $event_element;
+ var that = this;
+
+ var defaults = {
+
+ onStart: function () {
+
+ },
+
+ onEnd: function () {
+
+ },
+
+ onSkip: function () {
+
+ },
+
+ onNext: function () {
+
+ }
+ };
+
+ var options = $.extend(defaults, _options);
+ var data = [];
+ var current_step = 0;
+
+ $body = $('body');
+
+
+ /********************* PRIVATE METHODS ***************************************/
+
+ var init = function () {
+
+ if ($('.enjoyhint')) {
+
+ $('.enjoyhint').remove();
+ }
+
+ $body.css({'overflow':'hidden'});
+ $body.addClass('enjoyhint-tutorial');
+
+ $(document).on("touchmove",lockTouch);
+
+ $body.enjoyhint({
+
+ onNextClick: function () {
+
+ if (data[current_step].onNext && typeof data[current_step].onNext === 'function') {
+ data[current_step].onNext();
+ }
+
+ nextStep();
+ },
+
+ onSkipClick: function () {
+
+ options.onSkip();
+ skipAll();
+ }
+ });
+ };
+
+ var lockTouch = function(e) {
+
+ e.preventDefault();
+ };
+
+ var destroyEnjoy = function () {
+
+ $('.enjoyhint').remove();
+ $body.css({'overflow':'auto'});
+ $body.removeClass('enjoyhint-tutorial');
+ $(document).off("touchmove", lockTouch);
+ };
+
+ that.clear = function(){
+
+ var $nextBtn = $('.enjoyhint_next_btn');
+ var $skipBtn = $('.enjoyhint_skip_btn');
+
+ $nextBtn.removeClass(that.nextUserClass);
+ $nextBtn.text("Next");
+ $skipBtn.removeClass(that.skipUserClass);
+ $skipBtn.text("Skip");
+ };
+
+ var stepAction = function () {
+
+ if (!(data && data[current_step])) {
+
+ $body.enjoyhint('hide');
+ options.onEnd();
+ destroyEnjoy();
+ return;
+ }
+
+ options.onNext();
+
+ var $enjoyhint = $('.enjoyhint');
+ var step_data = data[current_step];
+
+ // Remove all classes
+ $enjoyhint.removeClass();
+ $enjoyhint.addClass("enjoyhint enjoyhint-step-" + (current_step + 1));
+ if (step_data.clickable) $enjoyhint.addClass('-clickable');
+ if (step_data.containerClass) $enjoyhint.addClass(step_data.containerClass);
+
+
+ if (step_data.onBeforeStart && typeof step_data.onBeforeStart === 'function') {
+
+ step_data.onBeforeStart();
+ }
+
+ var timeout = step_data.timeout || 0;
+
+ setTimeout(function () {
+
+ if (!step_data.selector) {
+
+ for (var prop in step_data) {
+
+ if (step_data.hasOwnProperty(prop) && prop.split(" ")[1]) {
+
+ step_data.selector = prop.split(" ")[1];
+ step_data.event = prop.split(" ")[0];
+
+ if (prop.split(" ")[0] == 'next' || prop.split(" ")[0] == 'auto' || prop.split(" ")[0] == 'custom') {
+
+ step_data.event_type = prop.split(" ")[0];
+ }
+
+ step_data.description = step_data[prop];
+ }
+ }
+ }
+
+ setTimeout(function () {
+
+ that.clear();
+ }, 250);
+
+ $(document.body).scrollTop(step_data.selector, step_data.scrollAnimationSpeed || 250, {offset: -100});
+
+ setTimeout(function () {
+
+ var $element = $(step_data.selector);
+ var event = makeEventName(step_data.event);
+
+ $body.enjoyhint('show');
+ $body.enjoyhint('hide_next');
+ $event_element = $element;
+
+ if (step_data.event_selector) {
+
+ $event_element = $(step_data.event_selector);
+ }
+
+ if (!step_data.event_type && step_data.event == "key") {
+
+ $element.keydown(function (event) {
+
+ if (event.which == step_data.keyCode) {
+
+ current_step++;
+ stepAction();
+ }
+ });
+ }
+
+ if (step_data.showNext == true) {
+
+ $body.enjoyhint('show_next');
+ }
+
+ if (step_data.showSkip == false) {
+
+ $body.enjoyhint('hide_skip');
+ } else {
+
+ $body.enjoyhint('show_skip');
+ }
+
+ if (step_data.showSkip == true) {
+
+ }
+
+ if (step_data.nextButton) {
+
+ var $nextBtn = $('.enjoyhint_next_btn');
+
+ $nextBtn.addClass(step_data.nextButton.className || "");
+ $nextBtn.text(step_data.nextButton.text || "Next");
+ that.nextUserClass = step_data.nextButton.className;
+ }
+
+ if (step_data.skipButton) {
+
+ var $skipBtn = $('.enjoyhint_skip_btn');
+
+ $skipBtn.addClass(step_data.skipButton.className || "");
+ $skipBtn.text(step_data.skipButton.text || "Skip");
+ that.skipUserClass = step_data.skipButton.className;
+ }
+
+ if (step_data.event_type) {
+
+ switch (step_data.event_type) {
+
+ case 'auto':
+
+ $element[step_data.event]();
+
+ switch (step_data.event) {
+
+ case 'click':
+ break;
+ }
+
+ current_step++;
+ stepAction();
+
+ return;
+ break;
+
+ case 'custom':
+
+ on(step_data.event, function () {
+
+ current_step++;
+ off(step_data.event);
+ stepAction();
+ });
+
+ break;
+
+ case 'next':
+
+ $body.enjoyhint('show_next');
+ break;
+ }
+
+ } else {
+
+ $event_element.on(event, function (e) {
+
+ if (step_data.keyCode && e.keyCode != step_data.keyCode) {
+
+ return;
+ }
+
+ current_step++;
+ $(this).off(event);
+
+ stepAction(); // clicked
+ });
+ }
+
+ var max_habarites = Math.max($element.outerWidth(), $element.outerHeight());
+ var radius = step_data.radius || Math.round(max_habarites / 2) + 5;
+ var offset = $element.offset();
+ var w = $element.outerWidth();
+ var h = $element.outerHeight();
+ var shape_margin = (step_data.margin !== undefined) ? step_data.margin : 10;
+
+ var coords = {
+ x: offset.left + Math.round(w / 2),
+ y: offset.top + Math.round(h / 2) - $(document).scrollTop()
+ };
+
+ var shape_data = {
+ enjoyHintElementSelector: step_data.selector,
+ center_x: coords.x,
+ center_y: coords.y,
+ text: step_data.description,
+ top: step_data.top,
+ bottom: step_data.bottom,
+ left: step_data.left,
+ right: step_data.right,
+ margin: step_data.margin,
+ scroll: step_data.scroll
+ };
+
+ if (step_data.shape && step_data.shape == 'circle') {
+
+ shape_data.shape = 'circle';
+ shape_data.radius = radius;
+ } else {
+
+ shape_data.radius = 0;
+ shape_data.width = w + shape_margin;
+ shape_data.height = h + shape_margin;
+ }
+
+ $body.enjoyhint('render_label_with_shape', shape_data, that.stop);
+ }, step_data.scrollAnimationSpeed + 20 || 270);
+ }, timeout);
+ };
+
+ var nextStep = function() {
+
+ current_step++;
+ stepAction();
+ };
+
+ var skipAll = function() {
+
+ var step_data = data[current_step];
+ var $element = $(step_data.selector);
+
+ off(step_data.event);
+ $element.off(makeEventName(step_data.event));
+
+ destroyEnjoy();
+ };
+
+ var makeEventName = function (name, is_custom) {
+
+ return name + (is_custom ? 'custom' : '') + '.enjoy_hint';
+ };
+
+ var on = function (event_name, callback) {
+
+ $body.on(makeEventName(event_name, true), callback);
+ };
+
+ var off = function (event_name) {
+
+ $body.off(makeEventName(event_name, true));
+ };
+
+
+ /********************* PUBLIC METHODS ***************************************/
+
+ window.addEventListener('resize', function() {
+
+ if ($event_element != null) {
+
+ $body.enjoyhint('redo_events_near_rect', $event_element[0].getBoundingClientRect());
+ }
+ });
+
+ that.stop = function() {
+
+ skipAll();
+ };
+
+ that.reRunScript = function(cs) {
+
+ current_step = cs;
+ stepAction();
+ };
+
+ that.runScript = function () {
+
+ current_step = 0;
+ options.onStart();
+ stepAction();
+ };
+
+ that.resumeScript = function () {
+
+ stepAction();
+ };
+
+ that.setCurrentStep = function(cs) {
+
+ current_step = cs;
+ };
+
+ that.getCurrentStep = function () {
+
+ return current_step;
+ };
+
+ that.trigger = function (event_name) {
+
+ switch (event_name) {
+
+ case 'next':
+
+ nextStep();
+ break;
+
+ case 'skip':
+
+ skipAll();
+ break;
+ }
+ };
+
+ that.setScript = function (_data) {
+
+ if (_data) {
+
+ data = _data;
+ }
+ };
+
+ //support deprecated API methods
+ that.set = function (_data) {
+
+ that.setScript(_data);
+ };
+
+ that.setSteps = function (_data) {
+
+ that.setScript(_data);
+ };
+
+ that.run = function () {
+
+ that.runScript();
+ };
+
+ that.resume = function () {
+
+ that.resumeScript();
+ };
+
+ init();
+ };;CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) {
+
+ if (w < 2 * r) r = w / 2;
+ if (h < 2 * r) r = h / 2;
+ this.beginPath();
+ this.moveTo(x + r, y);
+ this.arcTo(x + w, y, x + w, y + h, r);
+ this.arcTo(x + w, y + h, x, y + h, r);
+ this.arcTo(x, y + h, x, y, r);
+ this.arcTo(x, y, x + w, y, r);
+ this.closePath();
+ return this;
+ };
+
+ (function ($) {
+
+ var that;
+
+ var originalLabelLeft, originalLabelTop;
+ var originalArrowLeft, originalArrowTop;
+ var originalCenterX, originalCenterY;
+ var originalSkipbuttonLeft, originalSkipbuttonTop;
+ var prevWindowWidth, prevWindowHeight;
+ var originalWidth = window.innerWidth, originalHeight = window.innerHeight;
+
+ var methods = {
+
+ init: function (options) {
+
+ return this.each(function () {
+
+ var defaults = {
+
+ onNextClick: function () {
+
+ },
+ onSkipClick: function () {
+
+ },
+
+ animation_time: 800
+ };
+
+ this.enjoyhint_obj = {};
+ that = this.enjoyhint_obj;
+
+ that.resetComponentStuff = function() {
+
+ originalLabelLeft = null;
+ originalLabelTop = null;
+ originalArrowLeft = null;
+ originalArrowTop = null;
+ originalCenterX = null;
+ originalCenterY = null;
+ originalSkipbuttonLeft = null;
+ originalSkipbuttonTop = null;
+ prevWindowWidth = null;
+ prevWindowHeight = null;
+ originalWidth = window.innerWidth;
+ originalHeight = window.innerHeight;
+ };
+
+
+ var $that = $(this);
+ that.options = jQuery.extend(defaults, options);
+
+ //general classes
+ that.gcl = {
+
+ chooser: 'enjoyhint'
+ };
+
+ // classes
+ that.cl = {
+
+ enjoy_hint: 'enjoyhint',
+ hide: 'enjoyhint_hide',
+ disable_events_element: 'enjoyhint_disable_events',
+ btn: 'enjoyhint_btn',
+ skip_btn: 'enjoyhint_skip_btn',
+ close_btn: 'enjoyhint_close_btn',
+ next_btn: 'enjoyhint_next_btn',
+ main_canvas: 'enjoyhint_canvas',
+ main_svg: 'enjoyhint_svg',
+ svg_wrapper: 'enjoyhint_svg_wrapper',
+ svg_transparent: 'enjoyhint_svg_transparent',
+ kinetic_container: 'kinetic_container'
+ };
+
+ function makeSVG(tag, attrs) {
+
+ var el = document.createElementNS('http://www.w3.org/2000/svg', tag);
+
+ for (var k in attrs) {
+
+ el.setAttribute(k, attrs[k]);
+ }
+
+ return el;
+ }
+
+
+ // =======================================================================
+ // ========================---- enjoyhint ----==============================
+ // =======================================================================
+
+ that.canvas_size = {
+
+ w: $(window).width()*1.4,
+ h: $(window).height()*1.4
+ };
+
+ var canvas_id = "enj_canvas";
+
+ that.enjoyhint = $('
', {'class': that.cl.enjoy_hint + ' ' + that.cl.svg_transparent}).appendTo($that);
+ that.enjoyhint_svg_wrapper = $('
', {'class': that.cl.svg_wrapper + ' ' + that.cl.svg_transparent}).appendTo(that.enjoyhint);
+ that.$stage_container = $('
').appendTo(that.enjoyhint);
+ that.$canvas = $('