From 8c7ac00f140f61d23b082441d469f8208bf9edf8 Mon Sep 17 00:00:00 2001 From: mdtrooper Date: Fri, 8 Aug 2014 15:11:00 +0000 Subject: [PATCH] 2014-08-08 Miguel de Dios * include/functions.php, include/help/clippy/godmode_agentes_modificar_agente.php, include/help/clippy/homepage.php, include/help/clippy/godmode_agentes_configurar_agente.php, include/javascript/intro.js, include/javascript/introjs.css, include/javascript/clippy.js, include/functions_ui.php, include/functions_clippy.php, index.php, general/header.php: first version of the new feature a annoying clippy such as the lovely micro$oft mascot. git-svn-id: https://svn.code.sf.net/p/pandora/code/trunk@10393 c3f86ba8-e40f-0410-aaad-9ba5e7f4b01f --- pandora_console/ChangeLog | 15 + pandora_console/general/header.php | 13 +- pandora_console/include/functions.php | 17 + pandora_console/include/functions_clippy.php | 39 + pandora_console/include/functions_ui.php | 43 +- .../godmode_agentes_configurar_agente.php | 153 +++ .../godmode_agentes_modificar_agente.php | 100 ++ .../include/help/clippy/homepage.php | 69 ++ pandora_console/include/javascript/clippy.js | 8 + pandora_console/include/javascript/intro.js | 1034 +++++++++++++++++ .../include/javascript/introjs.css | 276 +++++ pandora_console/index.php | 8 + 12 files changed, 1769 insertions(+), 6 deletions(-) create mode 100644 pandora_console/include/functions_clippy.php create mode 100644 pandora_console/include/help/clippy/godmode_agentes_configurar_agente.php create mode 100644 pandora_console/include/help/clippy/godmode_agentes_modificar_agente.php create mode 100644 pandora_console/include/help/clippy/homepage.php create mode 100644 pandora_console/include/javascript/clippy.js create mode 100644 pandora_console/include/javascript/intro.js create mode 100644 pandora_console/include/javascript/introjs.css diff --git a/pandora_console/ChangeLog b/pandora_console/ChangeLog index 07a236b80f..e4cd837c60 100644 --- a/pandora_console/ChangeLog +++ b/pandora_console/ChangeLog @@ -1,3 +1,18 @@ +2014-08-08 Miguel de Dios + + * include/functions.php, + include/help/clippy/godmode_agentes_modificar_agente.php, + include/help/clippy/homepage.php, + include/help/clippy/godmode_agentes_configurar_agente.php, + include/javascript/intro.js, + include/javascript/introjs.css, + include/javascript/clippy.js, + include/functions_ui.php, + include/functions_clippy.php, + index.php, + general/header.php: first version of the new feature a annoying + clippy such as the lovely micro$oft mascot. + 2014-08-07 Alejandro Gallardo * pandoradb.sql, diff --git a/pandora_console/general/header.php b/pandora_console/general/header.php index aa8be8a91b..73e9bf45e8 100644 --- a/pandora_console/general/header.php +++ b/pandora_console/general/header.php @@ -122,7 +122,18 @@ config_check(); data[0][0] = $servers_link_open . $servers_check_img . $servers_link_close; + $table->data[0]['clippy'] = + '' . + html_print_image( + "images/heart_col.png", + true, + array("id" => 'clippy', + "class" => 'clippy', + "alt" => __('Clippy'), + 'title' => __('Clippy'))) . + ''; + $table->data[0][0] = $servers_link_open . + $servers_check_img . $servers_link_close; diff --git a/pandora_console/include/functions.php b/pandora_console/include/functions.php index e70b6ff52d..3d24fe88b8 100644 --- a/pandora_console/include/functions.php +++ b/pandora_console/include/functions.php @@ -768,6 +768,23 @@ function get_parameter_checkbox ($name, $default = '') { return get_parameter($name, 0); } +function get_cookie($name, $default = '') { + if (isset($_COOKIE[$name])) { + return $_COOKIE[$name]; + } + else { + return $default; + } +} + +function set_cookie($name, $value) { + if (is_null($value)) { + unset($_COOKIE[$value]); + setcookie($value, null, -1, '/'); + } + setcookie($name, $value); +} + /** * Get a parameter from a request. * diff --git a/pandora_console/include/functions_clippy.php b/pandora_console/include/functions_clippy.php new file mode 100644 index 0000000000..2f296896a8 --- /dev/null +++ b/pandora_console/include/functions_clippy.php @@ -0,0 +1,39 @@ + \ No newline at end of file diff --git a/pandora_console/include/functions_ui.php b/pandora_console/include/functions_ui.php index 581adecac4..884db5dcf8 100644 --- a/pandora_console/include/functions_ui.php +++ b/pandora_console/include/functions_ui.php @@ -1245,7 +1245,13 @@ function ui_process_page_head ($string, $bitfield) { } $output .= "\n\t"; + + + + + //////////////////////////////////////////////////////////////////// //Load CSS + //////////////////////////////////////////////////////////////////// if (empty ($config['css'])) { $config['css'] = array (); } @@ -1270,7 +1276,8 @@ function ui_process_page_head ($string, $bitfield) { } } - //First, if user has assigned a skin then try to use css files of skin subdirectory + //First, if user has assigned a skin then try to use css files of + //skin subdirectory $isFunctionSkins = enterprise_include_once ('include/functions_skins.php'); if (!$login_ok) { if ($isFunctionSkins !== ENTERPRISE_NOT_HOOK) { @@ -1278,6 +1285,7 @@ function ui_process_page_head ($string, $bitfield) { } } + $exists_css = false; if ($login_ok and $isFunctionSkins !== ENTERPRISE_NOT_HOOK) { //Checks if user's skin is available @@ -1305,11 +1313,16 @@ function ui_process_page_head ($string, $bitfield) { $config['css']); } + + // Add the jquery UI styles CSS $config['css']['jquery-UI'] = "include/styles/jquery-ui-1.10.0.custom.css"; - // Add the dialog styles CSS $config['css']['dialog'] = "include/styles/dialog.css"; + // Add the dialog styles CSS + $config['css']['dialog'] = "include/javascript/introjs.css"; + + //We can't load empty and we loaded (conditionally) ie $loaded = array ('', 'ie'); @@ -1333,24 +1346,33 @@ function ui_process_page_head ($string, $bitfield) { $output .= ''."\n\t"; } } + //////////////////////////////////////////////////////////////////// //End load CSS + //////////////////////////////////////////////////////////////////// + + + + //////////////////////////////////////////////////////////////////// //Load JS + //////////////////////////////////////////////////////////////////// if (empty ($config['js'])) { $config['js'] = array (); //If it's empty, false or not init set array to empty just in case } + //Pandora specific JavaScript should go first $config['js'] = array_merge (array ("pandora" => "include/javascript/pandora.js"), $config['js']); - //Load base64 javascript library $config['js']['base64'] = "include/javascript/encode_decode_base64.js"; - //Load webchat javascript library $config['js']['webchat'] = "include/javascript/webchat.js"; - //Load qrcode library $config['js']['qrcode'] = "include/javascript/qrcode.js"; + //Load intro.js library (for bubbles and clippy) + $config['js']['intro'] = "include/javascript/intro.js"; + $config['js']['clippy'] = "include/javascript/clippy.js"; + //Load other javascript //We can't load empty @@ -1370,9 +1392,15 @@ function ui_process_page_head ($string, $bitfield) { $output .= ''."\n\t"; } } + //////////////////////////////////////////////////////////////////// //End load JS + //////////////////////////////////////////////////////////////////// + + + //////////////////////////////////////////////////////////////////// //Load jQuery + //////////////////////////////////////////////////////////////////// if (empty ($config['jquery'])) { $config['jquery'] = array (); //If it's empty, false or not init set array to empty just in case } @@ -1422,6 +1450,11 @@ function ui_process_page_head ($string, $bitfield) { $output .= ''."\n\t"; } } + //////////////////////////////////////////////////////////////////// + //End load JQuery + //////////////////////////////////////////////////////////////////// + + if ($config['flash_charts']) { diff --git a/pandora_console/include/help/clippy/godmode_agentes_configurar_agente.php b/pandora_console/include/help/clippy/godmode_agentes_configurar_agente.php new file mode 100644 index 0000000000..5cba2d007a --- /dev/null +++ b/pandora_console/include/help/clippy/godmode_agentes_configurar_agente.php @@ -0,0 +1,153 @@ + '#clippy', + 'intro' => __('Now you must go to modules, don\'t worry I teach you.') + ); + $steps[] = array( + 'element'=> "img[alt='Modules']", + 'intro' => __('Please click in this tab.') + ); + break; + case 'create_module': + $steps = array(); + $steps[] = array( + 'element'=> '#clippy', + 'intro' => __('Now you must create the module, don\'t worry I teach you.') + ); + $steps[] = array( + 'element'=> "#moduletype", + 'intro' => __('Choose the network server module.') + ); + $steps[] = array( + 'element'=> "input[name='updbutton']", + 'intro' => __('And click in this button.') + ); + break; + case 'create_module_second_step': + $steps = array(); + $steps[] = array( + 'element'=> '#clippy', + 'intro' => __('We are going to fill the form.') + ); + $steps[] = array( + 'element'=> "#network_component_group", + 'intro' => __('Please choose the Network Management.') + ); + $steps[] = array( + 'element'=> "#network_component", + 'intro' => __('And choose the component with the name "Host Alive".') + ); + $steps[] = array( + 'element'=> "input[name='name']", + 'intro' => __('You can change the name.') + ); + $steps[] = array( + 'element'=> "input[name='ip_target']", + 'intro' => __('Check if this IP is the address of your machine.') + ); + $steps[] = array( + 'element'=> "input[name='crtbutton']", + 'intro' => __('And only to finish it is clicking this button.') + ); + break; + case 'create_module_third_step': + $steps = array(); + $steps[] = array( + 'element'=> '#clippy', + 'intro' => __('Now, your module is just created.
And the status color is blue.
This meaning of blue status is the module is not executed for first time.
In the next seconds if there is not a problem, the status color will change to red or green.') + ); + break; + } + + + + ?> + + \ No newline at end of file diff --git a/pandora_console/include/help/clippy/godmode_agentes_modificar_agente.php b/pandora_console/include/help/clippy/godmode_agentes_modificar_agente.php new file mode 100644 index 0000000000..0948758f61 --- /dev/null +++ b/pandora_console/include/help/clippy/godmode_agentes_modificar_agente.php @@ -0,0 +1,100 @@ + '#clippy', + 'intro' => __('I show how to monitoring a server.') + ); + $steps[] = array( + 'element'=> 'input[name="search"]', + 'intro' => __('Please type a agent to save the modules for monitoring a server.') + ); + $steps[] = array( + 'element'=> 'input[name="srcbutton"]', + 'intro' => __('Maybe if you typped correctly the name, you can see the agent.') + ); + break; + case 'choose_agent': + $steps = array(); + $steps[] = array( + 'element'=> '#clippy', + 'intro' => __('Please choose the agent that you have searched.') + ); + $steps[] = array( + 'element'=> '#agent_list', + 'intro' => __('Choose the agent, please click in the name.') + ); + break; + } + + + + ?> + + \ No newline at end of file diff --git a/pandora_console/include/help/clippy/homepage.php b/pandora_console/include/help/clippy/homepage.php new file mode 100644 index 0000000000..7458a03755 --- /dev/null +++ b/pandora_console/include/help/clippy/homepage.php @@ -0,0 +1,69 @@ + '#clippy', + 'intro' => __('Could you help you?

I am Pandorin, the annoying clippy for Pandora. You could follow my advices for to make common and basic tasks in Pandora.') + ); + $steps[] = array( + 'element'=> '#clippy', + 'intro' => __('What task do you want to do?') . '

' . + '' + ); + + ?> + + \ No newline at end of file diff --git a/pandora_console/include/javascript/clippy.js b/pandora_console/include/javascript/clippy.js new file mode 100644 index 0000000000..f7624e3638 --- /dev/null +++ b/pandora_console/include/javascript/clippy.js @@ -0,0 +1,8 @@ +function clippy_set_help(help_section) { + document.cookie = 'clippy=' + help_section; +} + +function clippy_go_link_show_help(link, help_section) { + document.cookie = 'clippy=' + help_section; + window.location.href = link; +} \ No newline at end of file diff --git a/pandora_console/include/javascript/intro.js b/pandora_console/include/javascript/intro.js new file mode 100644 index 0000000000..2b5b179f57 --- /dev/null +++ b/pandora_console/include/javascript/intro.js @@ -0,0 +1,1034 @@ +/** + * Intro.js v0.9.0 + * https://github.com/usablica/intro.js + * MIT licensed + * + * Copyright (C) 2013 usabli.ca - A weekend project by Afshin Mehrabani (@afshinmeh) + */ + +(function (root, factory) { + if (typeof exports === 'object') { + // CommonJS + factory(exports); + } else if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['exports'], factory); + } else { + // Browser globals + factory(root); + } +} (this, function (exports) { + //Default config/variables + var VERSION = '0.9.0'; + + /** + * IntroJs main class + * + * @class IntroJs + */ + function IntroJs(obj) { + this._targetElement = obj; + + this._options = { + /* Next button label in tooltip box */ + nextLabel: 'Next →', + /* Previous button label in tooltip box */ + prevLabel: '← Back', + /* Skip button label in tooltip box */ + skipLabel: 'Skip', + /* Done button label in tooltip box */ + doneLabel: 'Done', + /* Default tooltip box position */ + tooltipPosition: 'bottom', + /* Next CSS class for tooltip boxes */ + tooltipClass: '', + /* Close introduction when pressing Escape button? */ + exitOnEsc: true, + /* Close introduction when clicking on overlay layer? */ + exitOnOverlayClick: true, + /* Show step numbers in introduction? */ + showStepNumbers: true, + /* Let user use keyboard to navigate the tour? */ + keyboardNavigation: true, + /* Show tour control buttons? */ + showButtons: true, + /* Show tour bullets? */ + showBullets: true, + /* Scroll to highlighted element? */ + scrollToElement: true, + /* Set the overlay opacity */ + overlayOpacity: 0.8 + }; + } + + /** + * Initiate a new introduction/guide from an element in the page + * + * @api private + * @method _introForElement + * @param {Object} targetElm + * @returns {Boolean} Success or not? + */ + function _introForElement(targetElm) { + var introItems = [], + self = this; + + if (this._options.steps) { + //use steps passed programmatically + var allIntroSteps = []; + + for (var i = 0, stepsLength = this._options.steps.length; i < stepsLength; i++) { + var currentItem = _cloneObject(this._options.steps[i]); + //set the step + currentItem.step = introItems.length + 1; + //use querySelector function only when developer used CSS selector + if (typeof(currentItem.element) === 'string') { + //grab the element with given selector from the page + currentItem.element = document.querySelector(currentItem.element); + } + + //intro without element + if (typeof(currentItem.element) === 'undefined' || currentItem.element == null) { + var floatingElementQuery = document.querySelector(".introjsFloatingElement"); + + if (floatingElementQuery == null) { + floatingElementQuery = document.createElement('div'); + floatingElementQuery.className = 'introjsFloatingElement'; + + document.body.appendChild(floatingElementQuery); + } + + currentItem.element = floatingElementQuery; + currentItem.position = 'floating'; + } + + if (currentItem.element != null) { + introItems.push(currentItem); + } + } + + } else { + //use steps from data-* annotations + var allIntroSteps = targetElm.querySelectorAll('*[data-intro]'); + //if there's no element to intro + if (allIntroSteps.length < 1) { + return false; + } + + //first add intro items with data-step + for (var i = 0, elmsLength = allIntroSteps.length; i < elmsLength; i++) { + var currentElement = allIntroSteps[i]; + var step = parseInt(currentElement.getAttribute('data-step'), 10); + + if (step > 0) { + introItems[step - 1] = { + element: currentElement, + intro: currentElement.getAttribute('data-intro'), + step: parseInt(currentElement.getAttribute('data-step'), 10), + tooltipClass: currentElement.getAttribute('data-tooltipClass'), + position: currentElement.getAttribute('data-position') || this._options.tooltipPosition + }; + } + } + + //next add intro items without data-step + //todo: we need a cleanup here, two loops are redundant + var nextStep = 0; + for (var i = 0, elmsLength = allIntroSteps.length; i < elmsLength; i++) { + var currentElement = allIntroSteps[i]; + + if (currentElement.getAttribute('data-step') == null) { + + while (true) { + if (typeof introItems[nextStep] == 'undefined') { + break; + } else { + nextStep++; + } + } + + introItems[nextStep] = { + element: currentElement, + intro: currentElement.getAttribute('data-intro'), + step: nextStep + 1, + tooltipClass: currentElement.getAttribute('data-tooltipClass'), + position: currentElement.getAttribute('data-position') || this._options.tooltipPosition + }; + } + } + } + + //removing undefined/null elements + var tempIntroItems = []; + for (var z = 0; z < introItems.length; z++) { + introItems[z] && tempIntroItems.push(introItems[z]); // copy non-empty values to the end of the array + } + + introItems = tempIntroItems; + + //Ok, sort all items with given steps + introItems.sort(function (a, b) { + return a.step - b.step; + }); + + //set it to the introJs object + self._introItems = introItems; + + //add overlay layer to the page + if(_addOverlayLayer.call(self, targetElm)) { + //then, start the show + _nextStep.call(self); + + var skipButton = targetElm.querySelector('.introjs-skipbutton'), + nextStepButton = targetElm.querySelector('.introjs-nextbutton'); + + self._onKeyDown = function(e) { + if (e.keyCode === 27 && self._options.exitOnEsc == true) { + //escape key pressed, exit the intro + _exitIntro.call(self, targetElm); + //check if any callback is defined + if (self._introExitCallback != undefined) { + self._introExitCallback.call(self); + } + } else if(e.keyCode === 37) { + //left arrow + _previousStep.call(self); + } else if (e.keyCode === 39 || e.keyCode === 13) { + //right arrow or enter + _nextStep.call(self); + //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers + if(e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + } + }; + + self._onResize = function(e) { + _setHelperLayerPosition.call(self, document.querySelector('.introjs-helperLayer')); + }; + + if (window.addEventListener) { + if (this._options.keyboardNavigation) { + window.addEventListener('keydown', self._onKeyDown, true); + } + //for window resize + window.addEventListener("resize", self._onResize, true); + } else if (document.attachEvent) { //IE + if (this._options.keyboardNavigation) { + document.attachEvent('onkeydown', self._onKeyDown); + } + //for window resize + document.attachEvent("onresize", self._onResize); + } + } + return false; + } + + /* + * makes a copy of the object + * @api private + * @method _cloneObject + */ + function _cloneObject(object) { + if (object == null || typeof (object) != 'object' || typeof (object.nodeType) != 'undefined') { + return object; + } + var temp = {}; + for (var key in object) { + temp[key] = _cloneObject(object[key]); + } + return temp; + } + /** + * Go to specific step of introduction + * + * @api private + * @method _goToStep + */ + function _goToStep(step) { + //because steps starts with zero + this._currentStep = step - 2; + if (typeof (this._introItems) !== 'undefined') { + _nextStep.call(this); + } + } + + /** + * Go to next step on intro + * + * @api private + * @method _nextStep + */ + function _nextStep() { + this._direction = 'forward'; + + if (typeof (this._currentStep) === 'undefined') { + this._currentStep = 0; + } else { + ++this._currentStep; + } + + if ((this._introItems.length) <= this._currentStep) { + //end of the intro + //check if any callback is defined + if (typeof (this._introCompleteCallback) === 'function') { + this._introCompleteCallback.call(this); + } + _exitIntro.call(this, this._targetElement); + return; + } + + var nextStep = this._introItems[this._currentStep]; + if (typeof (this._introBeforeChangeCallback) !== 'undefined') { + this._introBeforeChangeCallback.call(this, nextStep.element); + } + + _showElement.call(this, nextStep); + } + + /** + * Go to previous step on intro + * + * @api private + * @method _nextStep + */ + function _previousStep() { + this._direction = 'backward'; + + if (this._currentStep === 0) { + return false; + } + + var nextStep = this._introItems[--this._currentStep]; + if (typeof (this._introBeforeChangeCallback) !== 'undefined') { + this._introBeforeChangeCallback.call(this, nextStep.element); + } + + _showElement.call(this, nextStep); + } + + /** + * Exit from intro + * + * @api private + * @method _exitIntro + * @param {Object} targetElement + */ + function _exitIntro(targetElement) { + //remove overlay layer from the page + var overlayLayer = targetElement.querySelector('.introjs-overlay'); + + //return if intro already completed or skipped + if (overlayLayer == null) { + return; + } + + //for fade-out animation + overlayLayer.style.opacity = 0; + setTimeout(function () { + if (overlayLayer.parentNode) { + overlayLayer.parentNode.removeChild(overlayLayer); + } + }, 500); + + //remove all helper layers + var helperLayer = targetElement.querySelector('.introjs-helperLayer'); + if (helperLayer) { + helperLayer.parentNode.removeChild(helperLayer); + } + + //remove intro floating element + var floatingElement = document.querySelector('.introjsFloatingElement'); + if (floatingElement) { + floatingElement.parentNode.removeChild(floatingElement); + } + + //remove `introjs-showElement` class from the element + var showElement = document.querySelector('.introjs-showElement'); + if (showElement) { + showElement.className = showElement.className.replace(/introjs-[a-zA-Z]+/g, '').replace(/^\s+|\s+$/g, ''); // This is a manual trim. + } + + //remove `introjs-fixParent` class from the elements + var fixParents = document.querySelectorAll('.introjs-fixParent'); + if (fixParents && fixParents.length > 0) { + for (var i = fixParents.length - 1; i >= 0; i--) { + fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, ''); + }; + } + + //clean listeners + if (window.removeEventListener) { + window.removeEventListener('keydown', this._onKeyDown, true); + } else if (document.detachEvent) { //IE + document.detachEvent('onkeydown', this._onKeyDown); + } + + //set the step to zero + this._currentStep = undefined; + } + + /** + * Render tooltip box in the page + * + * @api private + * @method _placeTooltip + * @param {Object} targetElement + * @param {Object} tooltipLayer + * @param {Object} arrowLayer + */ + function _placeTooltip(targetElement, tooltipLayer, arrowLayer, helperNumberLayer) { + var tooltipCssClass = '', + currentStepObj, + tooltipOffset, + targetElementOffset; + + //reset the old style + tooltipLayer.style.top = null; + tooltipLayer.style.right = null; + tooltipLayer.style.bottom = null; + tooltipLayer.style.left = null; + tooltipLayer.style.marginLeft = null; + tooltipLayer.style.marginTop = null; + + arrowLayer.style.display = 'inherit'; + + if (typeof(helperNumberLayer) != 'undefined' && helperNumberLayer != null) { + helperNumberLayer.style.top = null; + helperNumberLayer.style.left = null; + } + + //prevent error when `this._currentStep` is undefined + if (!this._introItems[this._currentStep]) return; + + //if we have a custom css class for each step + currentStepObj = this._introItems[this._currentStep]; + if (typeof (currentStepObj.tooltipClass) === 'string') { + tooltipCssClass = currentStepObj.tooltipClass; + } else { + tooltipCssClass = this._options.tooltipClass; + } + + tooltipLayer.className = ('introjs-tooltip ' + tooltipCssClass).replace(/^\s+|\s+$/g, ''); + + //custom css class for tooltip boxes + var tooltipCssClass = this._options.tooltipClass; + + currentTooltipPosition = this._introItems[this._currentStep].position; + switch (currentTooltipPosition) { + case 'top': + tooltipLayer.style.left = '15px'; + tooltipLayer.style.top = '-' + (_getOffset(tooltipLayer).height + 10) + 'px'; + arrowLayer.className = 'introjs-arrow bottom'; + break; + case 'right': + tooltipLayer.style.left = (_getOffset(targetElement).width + 20) + 'px'; + arrowLayer.className = 'introjs-arrow left'; + break; + case 'left': + if (this._options.showStepNumbers == true) { + tooltipLayer.style.top = '15px'; + } + tooltipLayer.style.right = (_getOffset(targetElement).width + 20) + 'px'; + arrowLayer.className = 'introjs-arrow right'; + break; + case 'floating': + arrowLayer.style.display = 'none'; + + //we have to adjust the top and left of layer manually for intro items without element + tooltipOffset = _getOffset(tooltipLayer); + + tooltipLayer.style.left = '50%'; + tooltipLayer.style.top = '50%'; + tooltipLayer.style.marginLeft = '-' + (tooltipOffset.width / 2) + 'px'; + tooltipLayer.style.marginTop = '-' + (tooltipOffset.height / 2) + 'px'; + + if (typeof(helperNumberLayer) != 'undefined' && helperNumberLayer != null) { + helperNumberLayer.style.left = '-' + ((tooltipOffset.width / 2) + 18) + 'px'; + helperNumberLayer.style.top = '-' + ((tooltipOffset.height / 2) + 18) + 'px'; + } + + break; + case 'bottom-right-aligned': + arrowLayer.className = 'introjs-arrow top-right'; + tooltipLayer.style.right = '0px'; + tooltipLayer.style.bottom = '-' + (_getOffset(tooltipLayer).height + 10) + 'px'; + break; + case 'bottom-middle-aligned': + targetElementOffset = _getOffset(targetElement); + tooltipOffset = _getOffset(tooltipLayer); + + arrowLayer.className = 'introjs-arrow top-middle'; + tooltipLayer.style.left = (targetElementOffset.width / 2 - tooltipOffset.width / 2) + 'px'; + tooltipLayer.style.bottom = '-' + (tooltipOffset.height + 10) + 'px'; + break; + case 'bottom-left-aligned': + // Bottom-left-aligned is the same as the default bottom + case 'bottom': + // Bottom going to follow the default behavior + default: + tooltipLayer.style.bottom = '-' + (_getOffset(tooltipLayer).height + 10) + 'px'; + arrowLayer.className = 'introjs-arrow top'; + break; + } + } + + /** + * Update the position of the helper layer on the screen + * + * @api private + * @method _setHelperLayerPosition + * @param {Object} helperLayer + */ + function _setHelperLayerPosition(helperLayer) { + if (helperLayer) { + //prevent error when `this._currentStep` in undefined + if (!this._introItems[this._currentStep]) return; + + var currentElement = this._introItems[this._currentStep], + elementPosition = _getOffset(currentElement.element), + widthHeightPadding = 10; + + if (currentElement.position == 'floating') { + widthHeightPadding = 0; + } + + //set new position to helper layer + helperLayer.setAttribute('style', 'width: ' + (elementPosition.width + widthHeightPadding) + 'px; ' + + 'height:' + (elementPosition.height + widthHeightPadding) + 'px; ' + + 'top:' + (elementPosition.top - 5) + 'px;' + + 'left: ' + (elementPosition.left - 5) + 'px;'); + } + } + + /** + * Show an element on the page + * + * @api private + * @method _showElement + * @param {Object} targetElement + */ + function _showElement(targetElement) { + + if (typeof (this._introChangeCallback) !== 'undefined') { + this._introChangeCallback.call(this, targetElement.element); + } + + var self = this, + oldHelperLayer = document.querySelector('.introjs-helperLayer'), + elementPosition = _getOffset(targetElement.element); + + if (oldHelperLayer != null) { + var oldHelperNumberLayer = oldHelperLayer.querySelector('.introjs-helperNumberLayer'), + oldtooltipLayer = oldHelperLayer.querySelector('.introjs-tooltiptext'), + oldArrowLayer = oldHelperLayer.querySelector('.introjs-arrow'), + oldtooltipContainer = oldHelperLayer.querySelector('.introjs-tooltip'), + skipTooltipButton = oldHelperLayer.querySelector('.introjs-skipbutton'), + prevTooltipButton = oldHelperLayer.querySelector('.introjs-prevbutton'), + nextTooltipButton = oldHelperLayer.querySelector('.introjs-nextbutton'); + + //hide the tooltip + oldtooltipContainer.style.opacity = 0; + + if (oldHelperNumberLayer != null) { + var lastIntroItem = this._introItems[(targetElement.step - 2 >= 0 ? targetElement.step - 2 : 0)]; + + if (lastIntroItem != null && (this._direction == 'forward' && lastIntroItem.position == 'floating') || (this._direction == 'backward' && targetElement.position == 'floating')) { + oldHelperNumberLayer.style.opacity = 0; + } + } + + //set new position to helper layer + _setHelperLayerPosition.call(self, oldHelperLayer); + + //remove `introjs-fixParent` class from the elements + var fixParents = document.querySelectorAll('.introjs-fixParent'); + if (fixParents && fixParents.length > 0) { + for (var i = fixParents.length - 1; i >= 0; i--) { + fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, ''); + }; + } + + //remove old classes + var oldShowElement = document.querySelector('.introjs-showElement'); + oldShowElement.className = oldShowElement.className.replace(/introjs-[a-zA-Z]+/g, '').replace(/^\s+|\s+$/g, ''); + //we should wait until the CSS3 transition is competed (it's 0.3 sec) to prevent incorrect `height` and `width` calculation + if (self._lastShowElementTimer) { + clearTimeout(self._lastShowElementTimer); + } + self._lastShowElementTimer = setTimeout(function() { + //set current step to the label + if (oldHelperNumberLayer != null) { + oldHelperNumberLayer.innerHTML = targetElement.step; + } + //set current tooltip text + oldtooltipLayer.innerHTML = targetElement.intro; + //set the tooltip position + _placeTooltip.call(self, targetElement.element, oldtooltipContainer, oldArrowLayer, oldHelperNumberLayer); + + //change active bullet + oldHelperLayer.querySelector('.introjs-bullets li > a.active').className = ''; + oldHelperLayer.querySelector('.introjs-bullets li > a[data-stepnumber="' + targetElement.step + '"]').className = 'active'; + + //show the tooltip + oldtooltipContainer.style.opacity = 1; + if (oldHelperNumberLayer) oldHelperNumberLayer.style.opacity = 1; + }, 350); + + } else { + var helperLayer = document.createElement('div'), + arrowLayer = document.createElement('div'), + tooltipLayer = document.createElement('div'), + tooltipTextLayer = document.createElement('div'), + bulletsLayer = document.createElement('div'), + buttonsLayer = document.createElement('div'); + + helperLayer.className = 'introjs-helperLayer'; + + //set new position to helper layer + _setHelperLayerPosition.call(self, helperLayer); + + //add helper layer to target element + this._targetElement.appendChild(helperLayer); + + arrowLayer.className = 'introjs-arrow'; + + tooltipTextLayer.className = 'introjs-tooltiptext'; + tooltipTextLayer.innerHTML = targetElement.intro; + + bulletsLayer.className = 'introjs-bullets'; + + if (this._options.showBullets === false) { + bulletsLayer.style.display = 'none'; + } + + var ulContainer = document.createElement('ul'); + + for (var i = 0, stepsLength = this._introItems.length; i < stepsLength; i++) { + var innerLi = document.createElement('li'); + var anchorLink = document.createElement('a'); + + anchorLink.onclick = function() { + self.goToStep(this.getAttribute('data-stepnumber')); + }; + + if (i === 0) anchorLink.className = "active"; + + anchorLink.href = 'javascript:void(0);'; + anchorLink.innerHTML = " "; + anchorLink.setAttribute('data-stepnumber', this._introItems[i].step); + + innerLi.appendChild(anchorLink); + ulContainer.appendChild(innerLi); + } + + bulletsLayer.appendChild(ulContainer); + + buttonsLayer.className = 'introjs-tooltipbuttons'; + if (this._options.showButtons === false) { + buttonsLayer.style.display = 'none'; + } + + tooltipLayer.className = 'introjs-tooltip'; + tooltipLayer.appendChild(tooltipTextLayer); + tooltipLayer.appendChild(bulletsLayer); + + //add helper layer number + if (this._options.showStepNumbers == true) { + var helperNumberLayer = document.createElement('span'); + helperNumberLayer.className = 'introjs-helperNumberLayer'; + helperNumberLayer.innerHTML = targetElement.step; + helperLayer.appendChild(helperNumberLayer); + } + tooltipLayer.appendChild(arrowLayer); + helperLayer.appendChild(tooltipLayer); + + //next button + var nextTooltipButton = document.createElement('a'); + + nextTooltipButton.onclick = function() { + if (self._introItems.length - 1 != self._currentStep) { + _nextStep.call(self); + } + }; + + nextTooltipButton.href = 'javascript:void(0);'; + nextTooltipButton.innerHTML = this._options.nextLabel; + + //previous button + var prevTooltipButton = document.createElement('a'); + + prevTooltipButton.onclick = function() { + if (self._currentStep != 0) { + _previousStep.call(self); + } + }; + + prevTooltipButton.href = 'javascript:void(0);'; + prevTooltipButton.innerHTML = this._options.prevLabel; + + //skip button + var skipTooltipButton = document.createElement('a'); + skipTooltipButton.className = 'introjs-button introjs-skipbutton'; + skipTooltipButton.href = 'javascript:void(0);'; + skipTooltipButton.innerHTML = this._options.skipLabel; + + skipTooltipButton.onclick = function() { + if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') { + self._introCompleteCallback.call(self); + } + + if (self._introItems.length - 1 != self._currentStep && typeof (self._introExitCallback) === 'function') { + self._introExitCallback.call(self); + } + + _exitIntro.call(self, self._targetElement); + }; + + buttonsLayer.appendChild(skipTooltipButton); + + //in order to prevent displaying next/previous button always + if (this._introItems.length > 1) { + buttonsLayer.appendChild(prevTooltipButton); + buttonsLayer.appendChild(nextTooltipButton); + } + + tooltipLayer.appendChild(buttonsLayer); + + //set proper position + _placeTooltip.call(self, targetElement.element, tooltipLayer, arrowLayer, helperNumberLayer); + } + + if (this._currentStep == 0 && this._introItems.length > 1) { + prevTooltipButton.className = 'introjs-button introjs-prevbutton introjs-disabled'; + nextTooltipButton.className = 'introjs-button introjs-nextbutton'; + skipTooltipButton.innerHTML = this._options.skipLabel; + } else if (this._introItems.length - 1 == this._currentStep || this._introItems.length == 1) { + skipTooltipButton.innerHTML = this._options.doneLabel; + prevTooltipButton.className = 'introjs-button introjs-prevbutton'; + nextTooltipButton.className = 'introjs-button introjs-nextbutton introjs-disabled'; + } else { + prevTooltipButton.className = 'introjs-button introjs-prevbutton'; + nextTooltipButton.className = 'introjs-button introjs-nextbutton'; + skipTooltipButton.innerHTML = this._options.skipLabel; + } + + //Set focus on "next" button, so that hitting Enter always moves you onto the next step + nextTooltipButton.focus(); + + //add target element position style + targetElement.element.className += ' introjs-showElement'; + + var currentElementPosition = _getPropValue(targetElement.element, 'position'); + if (currentElementPosition !== 'absolute' && + currentElementPosition !== 'relative') { + //change to new intro item + targetElement.element.className += ' introjs-relativePosition'; + } + + var parentElm = targetElement.element.parentNode; + while (parentElm != null) { + if (parentElm.tagName.toLowerCase() === 'body') break; + + //fix The Stacking Contenxt problem. + //More detail: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context + var zIndex = _getPropValue(parentElm, 'z-index'); + var opacity = parseFloat(_getPropValue(parentElm, 'opacity')); + if (/[0-9]+/.test(zIndex) || opacity < 1) { + parentElm.className += ' introjs-fixParent'; + } + + parentElm = parentElm.parentNode; + } + + if (!_elementInViewport(targetElement.element) && this._options.scrollToElement === true) { + var rect = targetElement.element.getBoundingClientRect(), + winHeight=_getWinSize().height, + top = rect.bottom - (rect.bottom - rect.top), + bottom = rect.bottom - winHeight; + + //Scroll up + if (top < 0 || targetElement.element.clientHeight > winHeight) { + window.scrollBy(0, top - 30); // 30px padding from edge to look nice + + //Scroll down + } else { + window.scrollBy(0, bottom + 100); // 70px + 30px padding from edge to look nice + } + } + + if (typeof (this._introAfterChangeCallback) !== 'undefined') { + this._introAfterChangeCallback.call(this, targetElement.element); + } + } + + /** + * Get an element CSS property on the page + * Thanks to JavaScript Kit: http://www.javascriptkit.com/dhtmltutors/dhtmlcascade4.shtml + * + * @api private + * @method _getPropValue + * @param {Object} element + * @param {String} propName + * @returns Element's property value + */ + function _getPropValue (element, propName) { + var propValue = ''; + if (element.currentStyle) { //IE + propValue = element.currentStyle[propName]; + } else if (document.defaultView && document.defaultView.getComputedStyle) { //Others + propValue = document.defaultView.getComputedStyle(element, null).getPropertyValue(propName); + } + + //Prevent exception in IE + if (propValue && propValue.toLowerCase) { + return propValue.toLowerCase(); + } else { + return propValue; + } + } + + /** + * Provides a cross-browser way to get the screen dimensions + * via: http://stackoverflow.com/questions/5864467/internet-explorer-innerheight + * + * @api private + * @method _getWinSize + * @returns {Object} width and height attributes + */ + function _getWinSize() { + if (window.innerWidth != undefined) { + return { width: window.innerWidth, height: window.innerHeight }; + } else { + var D = document.documentElement; + return { width: D.clientWidth, height: D.clientHeight }; + } + } + + /** + * Add overlay layer to the page + * http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport + * + * @api private + * @method _elementInViewport + * @param {Object} el + */ + function _elementInViewport(el) { + var rect = el.getBoundingClientRect(); + + return ( + rect.top >= 0 && + rect.left >= 0 && + (rect.bottom+80) <= window.innerHeight && // add 80 to get the text right + rect.right <= window.innerWidth + ); + } + + /** + * Add overlay layer to the page + * + * @api private + * @method _addOverlayLayer + * @param {Object} targetElm + */ + function _addOverlayLayer(targetElm) { + var overlayLayer = document.createElement('div'), + styleText = '', + self = this; + + //set css class name + overlayLayer.className = 'introjs-overlay'; + + //check if the target element is body, we should calculate the size of overlay layer in a better way + if (targetElm.tagName.toLowerCase() === 'body') { + styleText += 'top: 0;bottom: 0; left: 0;right: 0;position: fixed;'; + overlayLayer.setAttribute('style', styleText); + } else { + //set overlay layer position + var elementPosition = _getOffset(targetElm); + if (elementPosition) { + styleText += 'width: ' + elementPosition.width + 'px; height:' + elementPosition.height + 'px; top:' + elementPosition.top + 'px;left: ' + elementPosition.left + 'px;'; + overlayLayer.setAttribute('style', styleText); + } + } + + targetElm.appendChild(overlayLayer); + + overlayLayer.onclick = function() { + if (self._options.exitOnOverlayClick == true) { + _exitIntro.call(self, targetElm); + + //check if any callback is defined + if (self._introExitCallback != undefined) { + self._introExitCallback.call(self); + } + } + }; + + setTimeout(function() { + styleText += 'opacity: ' + self._options.overlayOpacity.toString() + ';'; + overlayLayer.setAttribute('style', styleText); + }, 10); + + return true; + } + + /** + * Get an element position on the page + * Thanks to `meouw`: http://stackoverflow.com/a/442474/375966 + * + * @api private + * @method _getOffset + * @param {Object} element + * @returns Element's position info + */ + function _getOffset(element) { + var elementPosition = {}; + + //set width + elementPosition.width = element.offsetWidth; + + //set height + elementPosition.height = element.offsetHeight; + + //calculate element top and left + var _x = 0; + var _y = 0; + while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) { + _x += element.offsetLeft; + _y += element.offsetTop; + element = element.offsetParent; + } + //set top + elementPosition.top = _y; + //set left + elementPosition.left = _x; + + return elementPosition; + } + + /** + * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1 + * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically + * + * @param obj1 + * @param obj2 + * @returns obj3 a new object based on obj1 and obj2 + */ + function _mergeOptions(obj1,obj2) { + var obj3 = {}; + for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; } + for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; } + return obj3; + } + + var introJs = function (targetElm) { + if (typeof (targetElm) === 'object') { + //Ok, create a new instance + return new IntroJs(targetElm); + + } else if (typeof (targetElm) === 'string') { + //select the target element with query selector + var targetElement = document.querySelector(targetElm); + + if (targetElement) { + return new IntroJs(targetElement); + } else { + throw new Error('There is no element with given selector.'); + } + } else { + return new IntroJs(document.body); + } + }; + + /** + * Current IntroJs version + * + * @property version + * @type String + */ + introJs.version = VERSION; + + //Prototype + introJs.fn = IntroJs.prototype = { + clone: function () { + return new IntroJs(this); + }, + setOption: function(option, value) { + this._options[option] = value; + return this; + }, + setOptions: function(options) { + this._options = _mergeOptions(this._options, options); + return this; + }, + start: function () { + _introForElement.call(this, this._targetElement); + return this; + }, + goToStep: function(step) { + _goToStep.call(this, step); + return this; + }, + nextStep: function() { + _nextStep.call(this); + return this; + }, + previousStep: function() { + _previousStep.call(this); + return this; + }, + exit: function() { + _exitIntro.call(this, this._targetElement); + }, + refresh: function() { + _setHelperLayerPosition.call(this, document.querySelector('.introjs-helperLayer')); + return this; + }, + onbeforechange: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introBeforeChangeCallback = providedCallback; + } else { + throw new Error('Provided callback for onbeforechange was not a function'); + } + return this; + }, + onchange: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introChangeCallback = providedCallback; + } else { + throw new Error('Provided callback for onchange was not a function.'); + } + return this; + }, + onafterchange: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introAfterChangeCallback = providedCallback; + } else { + throw new Error('Provided callback for onafterchange was not a function'); + } + return this; + }, + oncomplete: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introCompleteCallback = providedCallback; + } else { + throw new Error('Provided callback for oncomplete was not a function.'); + } + return this; + }, + onexit: function(providedCallback) { + if (typeof (providedCallback) === 'function') { + this._introExitCallback = providedCallback; + } else { + throw new Error('Provided callback for onexit was not a function.'); + } + return this; + } + }; + + exports.introJs = introJs; + return introJs; +})); diff --git a/pandora_console/include/javascript/introjs.css b/pandora_console/include/javascript/introjs.css new file mode 100644 index 0000000000..2aba35e623 --- /dev/null +++ b/pandora_console/include/javascript/introjs.css @@ -0,0 +1,276 @@ +.introjs-overlay { + position: absolute; + z-index: 999999; + background-color: #000; + opacity: 0; + background: -moz-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + background: -webkit-gradient(radial,center center,0px,center center,100%,color-stop(0%,rgba(0,0,0,0.4)),color-stop(100%,rgba(0,0,0,0.9))); + background: -webkit-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + background: -o-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + background: -ms-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + background: radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#66000000',endColorstr='#e6000000',GradientType=1); + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + filter: alpha(opacity=50); + -webkit-transition: all 0.3s ease-out; + -moz-transition: all 0.3s ease-out; + -ms-transition: all 0.3s ease-out; + -o-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + +.introjs-fixParent { + z-index: auto !important; + opacity: 1.0 !important; +} + +.introjs-showElement, +tr.introjs-showElement > td, +tr.introjs-showElement > th { + z-index: 9999999 !important; +} + +.introjs-relativePosition, +tr.introjs-showElement > td, +tr.introjs-showElement > th { + position: relative; +} + +.introjs-helperLayer { + position: absolute; + z-index: 9999998; + background-color: #FFF; + background-color: rgba(255,255,255,.9); + border: 1px solid #777; + border: 1px solid rgba(0,0,0,.5); + border-radius: 4px; + box-shadow: 0 2px 15px rgba(0,0,0,.4); + -webkit-transition: all 0.3s ease-out; + -moz-transition: all 0.3s ease-out; + -ms-transition: all 0.3s ease-out; + -o-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + +.introjs-helperNumberLayer { + position: absolute; + top: -16px; + left: -16px; + z-index: 9999999999 !important; + padding: 2px; + font-family: Arial, verdana, tahoma; + font-size: 13px; + font-weight: bold; + color: white; + text-align: center; + text-shadow: 1px 1px 1px rgba(0,0,0,.3); + background: #ff3019; /* Old browsers */ + background: -webkit-linear-gradient(top, #ff3019 0%, #cf0404 100%); /* Chrome10+,Safari5.1+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ff3019), color-stop(100%, #cf0404)); /* Chrome,Safari4+ */ + background: -moz-linear-gradient(top, #ff3019 0%, #cf0404 100%); /* FF3.6+ */ + background: -ms-linear-gradient(top, #ff3019 0%, #cf0404 100%); /* IE10+ */ + background: -o-linear-gradient(top, #ff3019 0%, #cf0404 100%); /* Opera 11.10+ */ + background: linear-gradient(to bottom, #ff3019 0%, #cf0404 100%); /* W3C */ + width: 20px; + height:20px; + line-height: 20px; + border: 3px solid white; + border-radius: 50%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3019', endColorstr='#cf0404', GradientType=0); /* IE6-9 */ + filter: progid:DXImageTransform.Microsoft.Shadow(direction=135, strength=2, color=ff0000); /* IE10 text shadows */ + box-shadow: 0 2px 5px rgba(0,0,0,.4); +} + +.introjs-arrow { + border: 5px solid white; + content:''; + position: absolute; +} +.introjs-arrow.top { + top: -10px; + border-top-color:transparent; + border-right-color:transparent; + border-bottom-color:white; + border-left-color:transparent; +} +.introjs-arrow.top-right { + top: -10px; + right: 10px; + border-top-color:transparent; + border-right-color:transparent; + border-bottom-color:white; + border-left-color:transparent; +} +.introjs-arrow.top-middle { + top: -10px; + left: 50%; + margin-left: -5px; + border-top-color:transparent; + border-right-color:transparent; + border-bottom-color:white; + border-left-color:transparent; +} +.introjs-arrow.right { + right: -10px; + top: 10px; + border-top-color:transparent; + border-right-color:transparent; + border-bottom-color:transparent; + border-left-color:white; +} +.introjs-arrow.bottom { + bottom: -10px; + border-top-color:white; + border-right-color:transparent; + border-bottom-color:transparent; + border-left-color:transparent; +} +.introjs-arrow.left { + left: -10px; + top: 10px; + border-top-color:transparent; + border-right-color:white; + border-bottom-color:transparent; + border-left-color:transparent; +} + +.introjs-tooltip { + position: absolute; + padding: 10px; + background-color: white; + min-width: 200px; + max-width: 300px; + border-radius: 3px; + box-shadow: 0 1px 10px rgba(0,0,0,.4); + -webkit-transition: opacity 0.1s ease-out; + -moz-transition: opacity 0.1s ease-out; + -ms-transition: opacity 0.1s ease-out; + -o-transition: opacity 0.1s ease-out; + transition: opacity 0.1s ease-out; +} + +.introjs-tooltipbuttons { + text-align: right; +} + +/* + Buttons style by http://nicolasgallagher.com/lab/css3-github-buttons/ + Changed by Afshin Mehrabani +*/ +.introjs-button { + position: relative; + overflow: visible; + display: inline-block; + padding: 0.3em 0.8em; + border: 1px solid #d4d4d4; + margin: 0; + text-decoration: none; + text-shadow: 1px 1px 0 #fff; + font: 11px/normal sans-serif; + color: #333; + white-space: nowrap; + cursor: pointer; + outline: none; + background-color: #ececec; + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f4f4f4), to(#ececec)); + background-image: -moz-linear-gradient(#f4f4f4, #ececec); + background-image: -o-linear-gradient(#f4f4f4, #ececec); + background-image: linear-gradient(#f4f4f4, #ececec); + -webkit-background-clip: padding; + -moz-background-clip: padding; + -o-background-clip: padding-box; + /*background-clip: padding-box;*/ /* commented out due to Opera 11.10 bug */ + -webkit-border-radius: 0.2em; + -moz-border-radius: 0.2em; + border-radius: 0.2em; + /* IE hacks */ + zoom: 1; + *display: inline; + margin-top: 10px; +} + +.introjs-button:hover { + border-color: #bcbcbc; + text-decoration: none; + box-shadow: 0px 1px 1px #e3e3e3; +} + +.introjs-button:focus, +.introjs-button:active { + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ececec), to(#f4f4f4)); + background-image: -moz-linear-gradient(#ececec, #f4f4f4); + background-image: -o-linear-gradient(#ececec, #f4f4f4); + background-image: linear-gradient(#ececec, #f4f4f4); +} + +/* overrides extra padding on button elements in Firefox */ +.introjs-button::-moz-focus-inner { + padding: 0; + border: 0; +} + +.introjs-skipbutton { + margin-right: 5px; + color: #7a7a7a; +} + +.introjs-prevbutton { + -webkit-border-radius: 0.2em 0 0 0.2em; + -moz-border-radius: 0.2em 0 0 0.2em; + border-radius: 0.2em 0 0 0.2em; + border-right: none; +} + +.introjs-nextbutton { + -webkit-border-radius: 0 0.2em 0.2em 0; + -moz-border-radius: 0 0.2em 0.2em 0; + border-radius: 0 0.2em 0.2em 0; +} + +.introjs-disabled, .introjs-disabled:hover, .introjs-disabled:focus { + color: #9a9a9a; + border-color: #d4d4d4; + box-shadow: none; + cursor: default; + background-color: #f4f4f4; + background-image: none; + text-decoration: none; +} + +.introjs-bullets { + text-align: center; +} +.introjs-bullets ul { + clear: both; + margin: 15px auto 0; + padding: 0; + display: inline-block; +} +.introjs-bullets ul li { + list-style: none; + float: left; + margin: 0 2px; +} +.introjs-bullets ul li a { + display: block; + width: 6px; + height: 6px; + background: #ccc; + border-radius: 10px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + text-decoration: none; +} +.introjs-bullets ul li a:hover { + background: #999; +} +.introjs-bullets ul li a.active { + background: #999; +} +.introjsFloatingElement { + position: absolute; + height: 0; + width: 0; + left: 50%; + top: 50%; +} diff --git a/pandora_console/index.php b/pandora_console/index.php index 2bedee90cb..54cc52a913 100644 --- a/pandora_console/index.php +++ b/pandora_console/index.php @@ -355,6 +355,7 @@ if (isset ($_GET["bye"])) { * Load here, because if not, some extensions not load well, I don't why. */ +$config['logged'] = false; extensions_load_extensions ($config['extensions']); if ($process_login) { /* Call all extensions login function */ @@ -376,6 +377,8 @@ if ($process_login) { //Set the initial global counter for chat. users_get_last_global_counter('session'); + + $config['logged'] = true; } //Get old parameters before navigation. @@ -585,6 +588,11 @@ if ($config["pure"] == 0) { require ("general/footer.php"); echo ''; } + +/// Clippy function +require ('include/functions_clippy.php'); +clippy_start($sec2); + while (@ob_end_flush ()); db_print_database_debug ();