Repository: mirasvit/module-profiler
Branch: master
Commit: 874c2d85d18a
Files: 53
Total size: 144.5 KB
Directory structure:
gitextract_tcnu8bym/
├── LICENSE.txt
├── README.md
├── composer.json
├── registration.php
└── src/
└── Profiler/
├── Api/
│ └── Data/
│ └── ProfileInterface.php
├── Block/
│ ├── Context.php
│ ├── Profile/
│ │ ├── Listing.php
│ │ └── View.php
│ ├── Tab/
│ │ ├── Code.php
│ │ ├── Database.php
│ │ ├── IO.php
│ │ └── TabInterface.php
│ └── Toolbar.php
├── Console/
│ └── Command/
│ ├── AbstractCommand.php
│ ├── AllowIpsCommand.php
│ ├── DisableCommand.php
│ ├── EnableCommand.php
│ └── StatusCommand.php
├── Controller/
│ ├── Profile/
│ │ ├── Index.php
│ │ └── View.php
│ └── Profile.php
├── Helper/
│ └── Format.php
├── Model/
│ ├── Config.php
│ ├── Driver/
│ │ └── Output/
│ │ └── Html.php
│ └── Storage.php
├── Profile/
│ ├── Code.php
│ ├── Context.php
│ ├── Database.php
│ ├── General.php
│ ├── Meta.php
│ └── Pool.php
├── etc/
│ ├── di.xml
│ ├── frontend/
│ │ └── routes.xml
│ └── module.xml
└── view/
└── base/
├── layout/
│ ├── profiler_profile_index.xml
│ └── profiler_profile_view.xml
├── page_layout/
│ └── profiler.xml
├── templates/
│ ├── profile/
│ │ ├── listing.phtml
│ │ └── view.phtml
│ ├── root.phtml
│ ├── tab/
│ │ ├── code.phtml
│ │ ├── database.phtml
│ │ └── io.phtml
│ └── toolbar.phtml
└── web/
├── css/
│ ├── less/
│ │ ├── _module/
│ │ │ ├── _base.less
│ │ │ ├── _etc.less
│ │ │ └── _tabs.less
│ │ └── _module.less
│ └── module.css
└── js/
├── lib/
│ ├── jquery.filtertable.js
│ ├── jquery.tablesorter.js
│ └── jquery.treetable.js
└── table.js
================================================
FILE CONTENTS
================================================
================================================
FILE: LICENSE.txt
================================================
Open Software License ("OSL") v. 3.0
This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work:
Licensed under the Open Software License version 3.0
1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following:
1. to reproduce the Original Work in copies, either alone or as part of a collective work;
2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work;
3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License;
4. to perform the Original Work publicly; and
5. to display the Original Work publicly.
2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works.
3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work.
4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license.
5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c).
6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work.
7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer.
8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation.
9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c).
10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware.
11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License.
12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License.
13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable.
14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You.
16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under <insert your license name here>" or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process.
================================================
FILE: README.md
================================================
# Magento 2 Profiler Module #
## Installation
Log in to the Magento server, go to your Magento install dir and run these commands:
```
composer require mirasvit/module-profiler
php -f bin/magento module:enable Mirasvit_Profiler
php -f bin/magento setup:upgrade
php -f bin/magento mirasvit:profiler:enable
```
## Usage
```
php -f bin/magento mirasvit:profiler:enable # Enable profiler
php -f bin/magento mirasvit:profiler:disable # Disable profiler
php -f bin/magento mirasvit:profiler:status # Current status
php -f bin/magento mirasvit:profiler:allow-ips 127.0.0.1 192.268.22.11 # Allow only specified IPs
php -f bin/magento mirasvit:profiler:allow-ips --none # Remove IP restriction
```
## Demo
[http://profiler.m2.mirasvit.com/](http://profiler.m2.mirasvit.com/)
[http://profiler.m2.mirasvit.com/profiler/profile/index/](http://profiler.m2.mirasvit.com/profiler/profile/index/)
## Screenshots
### Magento 2 Code Profiler and Database Profiler

## Licence
[Open Software License (OSL 3.0)](http://opensource.org/licenses/osl-3.0.php)
## 1.0.8
*(2020-10-08)
#### Improvements
* M2.4
---
## 1.0.6
*(2017-09-28)*
#### Improvements
* M2.2
#### Fixed
* Issue #20
---
## 1.0.5
*(2017-09-07)*
* PHP 5.6.x
---
## 1.0.3, 1.0.4
*(2017-09-06)*
* Issues with less compilation
---
## 1.0.2
*(2017-09-05)*
* Significant changes in UI
---
## 1.0.1
*(2017-03-30)*
* Improve styles load mechanism
---
## 1.0.0
*(2017-03-30)*
* Initial release
================================================
FILE: composer.json
================================================
{
"name": "mirasvit/module-profiler",
"description": "Magento 2 Profiler",
"require": {
"magento/framework": "100.*|101.*|102.*|103.*",
"jdorn/sql-formatter": "^1.2",
"symfony/yaml": ">=2.2.4"
},
"version": "1.0.8",
"type": "magento2-module",
"license": [
"proprietary"
],
"autoload": {
"files": [
"./registration.php"
],
"psr-4": {
"Mirasvit\\Profiler\\": "src/Profiler"
}
}
}
================================================
FILE: registration.php
================================================
<?php
$_SERVER['MAGE_PROFILER_STAT'] = new \Magento\Framework\Profiler\Driver\Standard\Stat();
$canEnable = defined('BP');
if (PHP_SAPI == 'cli') {
global $argv;
if (isset($argv[1]) && substr($argv[1], 0, strlen('setup')) == 'setup') {
$canEnable = false;
}
}
if ($canEnable) {
\Magento\Framework\Profiler::applyConfig([
'drivers' => [
[
'output' => 'Mirasvit\Profiler\Model\Driver\Output\Html',
'stat' => $_SERVER['MAGE_PROFILER_STAT'],
],
],
], 'BP', false);
}
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Mirasvit_Profiler',
__DIR__ . '/src/Profiler'
);
================================================
FILE: src/Profiler/Api/Data/ProfileInterface.php
================================================
<?php
namespace Mirasvit\Profiler\Api\Data;
interface ProfileInterface
{
/**
* @return array
*/
public function dump();
}
================================================
FILE: src/Profiler/Block/Context.php
================================================
<?php
namespace Mirasvit\Profiler\Block;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\App\ResourceConnection;
use Mirasvit\Profiler\Model\Storage;
class Context
{
private $request;
private $storage;
public function __construct(
RequestInterface $request,
Storage $storage
) {
$this->request = $request;
$this->storage = $storage;
}
public function getProfile($id = false)
{
if (!$id) {
$id = $this->request->getParam('id');
}
return $this->storage->load($id);
}
// /**
// * @param \Magento\Framework\Profiler\Driver\Standard\Stat $stat
// * @return $this
// */
// public function setProfilerStat($stat)
// {
// $this->profilerStat = $stat;
//
// return $this;
// }
//
// /**
// * @return \Magento\Framework\Profiler\Driver\Standard\Stat
// */
// public function getProfilerStat()
// {
// return $this->profilerStat;
// }
//
// /**
// * @return \Zend_Db_Profiler
// */
// public function getDbProfiler()
// {
// return $this->resourceConnection->getConnection('read')
// ->getProfiler();
// }
}
================================================
FILE: src/Profiler/Block/Profile/Listing.php
================================================
<?php
namespace Mirasvit\Profiler\Block\Profile;
use Magento\Framework\View\Element\Template\Context as TemplateContext;
use Magento\Framework\View\Element\Template;
use Mirasvit\Profiler\Model\Storage;
class Listing extends Template
{
/**
* @var Storage
*/
private $storage;
public function __construct(
Storage $storage,
Template\Context $context,
array $data = []
) {
$this->storage = $storage;
parent::__construct($context, $data);
}
public function getList()
{
return $this->storage->getList();
}
}
================================================
FILE: src/Profiler/Block/Profile/View.php
================================================
<?php
namespace Mirasvit\Profiler\Block\Profile;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\View\Element\Template;
use Mirasvit\Profiler\Block\Tab\TabInterface;
use Mirasvit\Profiler\Model\Storage;
class View extends Template
{
/**
* @var RequestInterface
*/
private $request;
/**
* @var Storage
*/
private $storage;
/**
* @var array
*/
private $tabs;
public function __construct(
Storage $storage,
Template\Context $context,
array $tabs = []
) {
$this->request = $context->getRequest();
$this->storage = $storage;
$this->tabs = $tabs;
parent::__construct($context);
}
public function getProfile()
{
return $this->storage->load($this->getRequest()->getParam('id'));
}
/**
* @return TabInterface[]
*/
public function getTabs()
{
return $this->tabs;
}
}
================================================
FILE: src/Profiler/Block/Tab/Code.php
================================================
<?php
namespace Mirasvit\Profiler\Block\Tab;
use Magento\Framework\View\Element\Template\Context as TemplateContext;
use Magento\Framework\View\Element\Template;
use Mirasvit\Profiler\Block\Context;
class Code extends Template implements TabInterface
{
/**
* @var string
*/
protected $_template = 'tab/code.phtml';
/**
* @var Context
*/
protected $context;
public function __construct(
Context $context,
TemplateContext $templateContext,
array $data = []
) {
$this->context = $context;
parent::__construct($templateContext, $data);
}
/**
* {@inheritdoc}
*/
public function getLabel()
{
return 'Performance';
}
/**
* {@inheritdoc}
*/
public function getIcon()
{
return 'clock-o';
}
/**
* @return array
*/
public function getCodeDump()
{
return $this->context->getProfile()['code'];
}
public function getFlameGraphJson() {
$frameGraph = [
'name' => 'root',
'value' => $this->getGeneralDump()[\Mirasvit\Profiler\Profile\General::EXECUTION_TIME] / 1000,
'children' => [],
];
foreach ($this->context->getProfile()['code'] as $path => $data) {
$data['value'] = $data['sum'];
$data['children'] = [];
$selectedNode = &$frameGraph;
foreach(explode('->', $path) as $node) {
$children = &$selectedNode['children'];
if (! isset($children[$node])) {
$data['name'] = $node;
$children[$node] = $data;
}
$selectedNode = &$children[$node];
}
}
return \json_encode($this->_removeChildrenKeys($frameGraph));
}
private function _removeChildrenKeys($node) {
$node['children'] = array_values($node['children']);
foreach ($node['children'] as &$child) {
$child = $this->_removeChildrenKeys($child);
}
return $node;
}
/**
* @return array
*/
public function getGeneralDump()
{
return $this->context->getProfile()['general'];
}
public function getLevel($timerId)
{
return substr_count($timerId, '->');
}
/**
* @param int $timerId
* @return string
*/
public function renderTimerId($timerId)
{
$nestingSep = preg_quote('->', '/');
return preg_replace('/.+?' . $nestingSep . '/', '', $timerId);
}
/**
* @param int $timerId
* @return string
*/
public function getParentTimerId($timerId)
{
$timerId = explode('->', $timerId);
array_pop($timerId);
return implode('->', $timerId);
}
/**
* @param int $timerId
* @return float
*/
public function getTimerLength($timerId)
{
return 0;
$total = microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
return round($this->getStat()->fetch($timerId, 'sum') / $total * 100, 2);
}
/**
* @return float
*/
public function getTotalTime()
{
return microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'];
}
}
================================================
FILE: src/Profiler/Block/Tab/Database.php
================================================
<?php
namespace Mirasvit\Profiler\Block\Tab;
use Magento\Framework\View\Element\Template\Context as TemplateContext;
use Magento\Framework\View\Element\Template;
use Magento\Framework\App\ResourceConnection;
use Mirasvit\Profiler\Block\Context;
class Database extends Template implements TabInterface
{
/**
* @var string
*/
protected $_template = 'tab/database.phtml';
/**
* @var Context
*/
private $context;
public function __construct(
Context $context,
TemplateContext $templateContext
) {
$this->context = $context;
parent::__construct($templateContext);
}
/**
* {@inheritdoc}
*/
public function getLabel()
{
return __('Database');
}
/**
* {@inheritdoc}
*/
public function getIcon()
{
return 'database';
}
/**
* @return array
*/
public function getDump()
{
return $this->context->getProfile()['database'];
}
/**
* @return array
*/
public function getSlowQueries()
{
$queries = [];
/** @var \Zend_Db_Profiler_Query $query */
foreach ($this->getDbProfiler()->getQueryProfiles() as $queryId => $query) {
$queries[$queryId] = $query->getElapsedSecs();
}
arsort($queries);
$queries = array_slice($queries, 0, 5, true);
return $queries;
}
}
================================================
FILE: src/Profiler/Block/Tab/IO.php
================================================
<?php
namespace Mirasvit\Profiler\Block\Tab;
use Magento\Framework\View\Element\Template\Context as TemplateContext;
use Magento\Framework\View\Element\Template;
use Mirasvit\Profiler\Block\Context;
class IO extends Template implements TabInterface
{
/**
* @var string
*/
protected $_template = 'tab/io.phtml';
/**
* @var Context
*/
protected $context;
public function __construct(
Context $context,
TemplateContext $templateContext,
array $data = []
) {
$this->context = $context;
parent::__construct($templateContext, $data);
}
/**
* {@inheritdoc}
*/
public function getLabel()
{
return 'Request / Response';
}
/**
* {@inheritdoc}
*/
public function getIcon()
{
return 'globe';
}
/**
* @return array
*/
public function getDump()
{
return $this->context->getProfile();
}
}
================================================
FILE: src/Profiler/Block/Tab/TabInterface.php
================================================
<?php
namespace Mirasvit\Profiler\Block\Tab;
interface TabInterface
{
/**
* @return string
*/
public function getIcon();
/**
* @return string
*/
public function getLabel();
}
================================================
FILE: src/Profiler/Block/Toolbar.php
================================================
<?php
namespace Mirasvit\Profiler\Block;
use Magento\Framework\View\Element\Template\Context as TemplateContext;
use Magento\Framework\View\Element\Template;
use Magento\Framework\Url;
/**
* @method string getProfileId()
*/
class Toolbar extends Template
{
/**
* @var string
*/
protected $_template = 'toolbar.phtml';
/**
* @var Context
*/
private $context;
/**
* @var Url
*/
private $urlHelper;
public function __construct(
TemplateContext $templateContext,
Url $urlHelper,
Context $context
) {
$this->context = $context;
$this->urlHelper = $urlHelper;
parent::__construct($templateContext);
}
/**
* @return array
*/
public function getDump()
{
return $this->context->getProfile($this->getProfileId());
}
/**
* @return string
*/
public function getProfileUrl()
{
return $this->urlHelper->getUrl('profiler/profile/view', ['id' => $this->getProfileId()]);
}
}
================================================
FILE: src/Profiler/Console/Command/AbstractCommand.php
================================================
<?php
namespace Mirasvit\Profiler\Console\Command;
use Magento\Framework\App\DeploymentConfig;
use Magento\Framework\App\State;
use Symfony\Component\Console\Command\Command;
abstract class AbstractCommand extends Command
{
/**
* App state
*
* @var \Magento\Framework\App\State
*/
protected $appState;
/**
* {@inheritdoc}
*
* @param State $appState
*/
public function __construct(
State $appState
) {
$this->appState = $appState;
parent::__construct();
}
}
================================================
FILE: src/Profiler/Console/Command/AllowIpsCommand.php
================================================
<?php
namespace Mirasvit\Profiler\Console\Command;
use Magento\Framework\App\State;
use Magento\Framework\Exception\LocalizedException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Mirasvit\Profiler\Model\Config;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
class AllowIpsCommand extends AbstractCommand
{
/**
* @var Config
*/
protected $config;
public function __construct(
Config $config,
State $appState
) {
$this->config = $config;
parent::__construct($appState);
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$arguments = [
new InputArgument(
'ip',
InputArgument::OPTIONAL | InputArgument::IS_ARRAY,
'Allowed IP addresses'
),
];
$options = [
new InputOption(
'none',
null,
InputOption::VALUE_NONE,
'Clear allowed IP addresses'
),
];
$this->setName('mirasvit:profiler:allow-ips')
->setDescription('Enable profiler only for specified IPs')
->setDefinition(array_merge($arguments, $options));
parent::configure();
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
try{
$this->appState->setAreaCode('empty');
}catch (LocalizedException $e){}
if (!$input->getOption('none')) {
$addresses = $input->getArgument('ip');
if (!empty($addresses)) {
$this->config->setAddresses(implode(',', $addresses));
$output->writeln(
'<info>Set exempt IP-addresses: ' . implode(', ', $this->config->getAddressInfo()) .
'</info>'
);
}
} else {
$this->config->setAddresses('');
$output->writeln('<info>Set exempt IP-addresses: none</info>');
}
}
}
================================================
FILE: src/Profiler/Console/Command/DisableCommand.php
================================================
<?php
namespace Mirasvit\Profiler\Console\Command;
use Magento\Framework\App\State;
use Magento\Framework\Exception\LocalizedException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Mirasvit\Profiler\Model\Config;
class DisableCommand extends AbstractCommand
{
/**
* @var Config
*/
private $config;
public function __construct(
Config $config,
State $appState
) {
$this->config = $config;
parent::__construct($appState);
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->setName('mirasvit:profiler:disable')
->setDescription('Disable profiler')
->setDefinition([]);
parent::configure();
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
try {
$this->appState->setAreaCode('empty');
} catch (LocalizedException $e) {
}
$this->config->disableProfiler();
$output->writeln('<info>Status: ' . ($this->config->isEnabled() ? 'Enabled' : 'Disabled') . '</info>');
}
}
================================================
FILE: src/Profiler/Console/Command/EnableCommand.php
================================================
<?php
namespace Mirasvit\Profiler\Console\Command;
use Magento\Framework\App\State;
use Magento\Framework\Exception\LocalizedException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Mirasvit\Profiler\Model\Config;
class EnableCommand extends AbstractCommand
{
/**
* @var Config
*/
private $config;
public function __construct(
Config $config,
State $appState
) {
$this->config = $config;
parent::__construct($appState);
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->setName('mirasvit:profiler:enable')
->setDescription('Enable profiler')
->setDefinition([]);
parent::configure();
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
try {
$this->appState->setAreaCode('empty');
} catch (LocalizedException $e) {
}
$this->config->enableProfiler();
$output->writeln('<info>Status: ' . ($this->config->isEnabled() ? 'Enabled' : 'Disabled') . '</info>');
}
}
================================================
FILE: src/Profiler/Console/Command/StatusCommand.php
================================================
<?php
namespace Mirasvit\Profiler\Console\Command;
use Magento\Framework\App\State;
use Magento\Framework\Exception\LocalizedException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Mirasvit\Profiler\Model\Config;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
class StatusCommand extends AbstractCommand
{
/**
* @var Config
*/
private $config;
public function __construct(
Config $config,
State $appState
) {
$this->config = $config;
parent::__construct($appState);
}
/**
* {@inheritdoc}
*/
protected function configure()
{
$this->setName('mirasvit:profiler:status')
->setDescription('Profiler status')
->setDefinition([]);
parent::configure();
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
try{
$this->appState->setAreaCode('empty');
}catch (LocalizedException $e){}
$output->writeln('<info>Status: ' . ($this->config->isEnabled() ? 'Enabled' : 'Disabled') . '</info>');
$output->writeln('<info>IPs: ' . implode(', ', $this->config->getAddressInfo()) . '</info>');
}
}
================================================
FILE: src/Profiler/Controller/Profile/Index.php
================================================
<?php
namespace Mirasvit\Profiler\Controller\Profile;
use Mirasvit\Profiler\Controller\Profile;
use Magento\Framework\Controller\ResultFactory;
class Index extends Profile
{
public function execute()
{
/** @var \Magento\Framework\View\Result\Page $resultPage */
$resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE, [
'template' => 'Mirasvit_Profiler::root.phtml',
]);
$resultPage->getConfig()->getTitle()->set(__('Profiler'));
return $resultPage;
}
}
================================================
FILE: src/Profiler/Controller/Profile/View.php
================================================
<?php
namespace Mirasvit\Profiler\Controller\Profile;
use Mirasvit\Profiler\Controller\Profile;
use Magento\Framework\Controller\ResultFactory;
class View extends Profile
{
public function execute()
{
/** @var \Magento\Framework\View\Result\Page $resultPage */
$resultPage = $this->resultFactory->create(ResultFactory::TYPE_PAGE, [
'template' => 'Mirasvit_Profiler::root.phtml',
]);
$resultPage->getConfig()->getTitle()->set(__('Profiler'));
return $resultPage;
}
}
================================================
FILE: src/Profiler/Controller/Profile.php
================================================
<?php
namespace Mirasvit\Profiler\Controller;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
abstract class Profile extends Action
{
public function __construct(
Context $context
) {
parent::__construct($context);
}
}
================================================
FILE: src/Profiler/Helper/Format.php
================================================
<?php
namespace Mirasvit\Profiler\Helper;
use Magento\Framework\App\Helper\AbstractHelper;
class Format extends AbstractHelper
{
/**
* @param float $number
* @param bool $isSeconds
* @return string
*/
public function formatTime($number, $isSeconds = false)
{
if ($isSeconds) {
$number *= 1000;
}
return number_format($number, 1, '.', ' ');
}
/**
* @param array|object $any
* @return string
*/
public function any($any)
{
if (is_array($any)) {
if (count($any)) {
return '<pre>' . print_r($any, true) . '</pre>';
}
} else {
return json_encode($any);
}
return '';
}
}
================================================
FILE: src/Profiler/Model/Config.php
================================================
<?php
namespace Mirasvit\Profiler\Model;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\DeploymentConfig\Writer as DeploymentConfigWriter;
use Magento\Framework\App\DeploymentConfig\Reader as DeploymentConfigReader;
use Magento\Framework\Config\File\ConfigFilePool;
use Magento\Config\Model\Config\Factory as ConfigFactory;
use Magento\Framework\App\Filesystem\DirectoryList;
class Config
{
CONST HTACCESS_ENV = 'SetEnv MAGE_PROFILER Mirasvit\Profiler\Model\Driver\Standard\Output\Html';
/**
* @var ScopeConfigInterface
*/
private $scopeConfig;
/**
* @var DeploymentConfigWriter
*/
private $deploymentConfigWriter;
/**
* @var DeploymentConfigWriter
*/
private $deploymentConfigReader;
/**
* @var ConfigFactory
*/
private $configFactory;
/**
* @var DirectoryList
*/
private $directoryList;
public function __construct(
DeploymentConfigWriter $deploymentConfigWriter,
DeploymentConfigReader $deploymentConfigReader,
ScopeConfigInterface $scopeConfig,
ConfigFactory $configFactory,
DirectoryList $directoryList
) {
$this->deploymentConfigWriter = $deploymentConfigWriter;
$this->deploymentConfigReader = $deploymentConfigReader;
$this->scopeConfig = $scopeConfig;
$this->configFactory = $configFactory;
$this->directoryList = $directoryList;
}
/**
* @return bool
*/
public function isEnabled()
{
return (bool)$this->scopeConfig->getValue('profiler/general/enable');
}
/**
* @return bool
*/
public function enableProfiler()
{
$config = $this->configFactory->create();
$config->setDataByPath('profiler/general/enable', true);
$config->save();
$this->enableDbProfiler();
return true;
}
/**
* @return bool
*/
public function disableProfiler()
{
$config = $this->configFactory->create();
$config->setDataByPath('profiler/general/enable', false);
$config->save();
$this->disableDbProfiler();
return true;
}
/**
* @return bool
* @throws \Exception
*/
public function enableDbProfiler()
{
$env = $this->deploymentConfigReader->load(ConfigFilePool::APP_ENV);
$env['db']['connection']['default']['profiler'] = [
'class' => '\\Magento\\Framework\\DB\\Profiler',
'enabled' => true,
];
$this->deploymentConfigWriter->saveConfig([ConfigFilePool::APP_ENV => $env], true);
return true;
}
/**
* @return bool
* @throws \Exception
*/
public function disableDbProfiler()
{
$env = $this->deploymentConfigReader->load(ConfigFilePool::APP_ENV);
unset($env['db']['connection']['default']['profiler']);
$this->deploymentConfigWriter->saveConfig([ConfigFilePool::APP_ENV => $env], true);
return true;
}
/**
* @param string $addresses
* @return bool
*/
public function setAddresses($addresses)
{
$config = $this->configFactory->create();
$config->setDataByPath('profiler/general/addresses', $addresses);
$config->save();
return true;
}
/**
* @return array
*/
public function getAddressInfo()
{
$addresses = $this->scopeConfig->getValue('profiler/general/addresses');
return array_filter(explode(',', $addresses));
}
public function getDumpPath()
{
$path = $this->directoryList->getPath('var').'/profiler/';
if (!file_exists($path)) {
mkdir($path);
}
return $path;
}
}
================================================
FILE: src/Profiler/Model/Driver/Output/Html.php
================================================
<?php
namespace Mirasvit\Profiler\Model\Driver\Output;
use Magento\Framework\Profiler\Driver\Standard\Stat;
use Magento\Framework\Profiler\Driver\Standard\OutputInterface;
use Magento\Framework\App\ObjectManager;
use Mirasvit\Profiler\Model\Storage;
class Html implements OutputInterface
{
/**
* {@inheritdoc}
*/
public function display(Stat $stat)
{
$objectManager = ObjectManager::getInstance();
/** @var \Mirasvit\Profiler\Model\Config $config */
$config = $objectManager->get('Mirasvit\Profiler\Model\Config');
if (!$config->isEnabled()) {
return;
}
$addresses = $config->getAddressInfo();
if (count($addresses) && isset($_SERVER['REMOTE_ADDR']) && !in_array($_SERVER['REMOTE_ADDR'], $addresses)) {
return;
}
if (isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], 'profiler') !== false) {
return;
}
/** @var \Mirasvit\Profiler\Model\Storage $storage */
$storage = $objectManager->get('Mirasvit\Profiler\Model\Storage');
$profileId = $storage->dump();
$isAjax = !empty($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
if (!$isAjax && PHP_SAPI != 'cli' && strpos($_SERVER['HTTP_ACCEPT'], 'text/html') !== false) {
/** @var \Magento\Framework\View\LayoutInterface $layout */
$layout = $objectManager->create('Magento\Framework\View\LayoutInterface');
echo $layout->createBlock('Mirasvit\Profiler\Block\Toolbar')
->setProfileId($profileId)
->toHtml();
}
}
}
================================================
FILE: src/Profiler/Model/Storage.php
================================================
<?php
namespace Mirasvit\Profiler\Model;
use Mirasvit\Profiler\Profile\Pool;
use Symfony\Component\Yaml\Dumper as YamlDumper;
use Symfony\Component\Yaml\Parser as YamlParser;
class Storage
{
/**
* @var Pool
*/
private $pool;
/**
* @var Config
*/
private $config;
public function __construct(
Pool $profilePool,
Config $config
) {
$this->pool = $profilePool;
$this->config = $config;
}
/**
* @return string
*/
public function dump()
{
$dump = [];
foreach ($this->pool->getProfiles() as $code => $profile) {
$dump[$code] = $profile->dump();
}
if (!isset($dump['meta'])) {
return false;
}
$meta = $dump['meta'];
$dt = (\DateTime::createFromFormat('U.u', microtime(true)));
$name = $dt->format('Y-m-d H:i:s.u');
$file = $this->config->getDumpPath() . $name . '.meta';
file_put_contents($file, (new YamlDumper())->dump($meta, 10));
$file = $this->config->getDumpPath() . $name . '.prof';
file_put_contents($file, (new YamlDumper())->dump($dump, 10));
$this->cleanup();
return $name;
}
/**
* @return array
*/
public function load($file)
{
$content = file_get_contents($this->config->getDumpPath() . '/' . $file . '.prof');
$dump = (new YamlParser())->parse($content);
return $dump;
}
public function getList()
{
$result = [];
$files = scandir($this->config->getDumpPath(), 1);
foreach ($files as $file) {
if ($file[0] == '.') {
continue;
}
if (pathinfo($file)['extension'] != 'meta') {
continue;
}
$content = file_get_contents($this->config->getDumpPath() . '/' . $file);
$meta = (new YamlParser())->parse($content);
$meta['ID'] = pathinfo($file)['filename'];
$result[] = $meta;
}
return $result;
}
public function cleanup()
{
$limit = 100;
$files = scandir($this->config->getDumpPath(), 1);
foreach ($files as $file) {
if ($file[0] == '.') {
continue;
}
if (pathinfo($file)['extension'] != 'meta') {
continue;
}
$limit--;
if ($limit < 0) {
$name = pathinfo($file)['filename'];
@unlink($this->config->getDumpPath() . '/' . $name . '.meta');
@unlink($this->config->getDumpPath() . '/' . $name . '.prof');
}
}
}
}
================================================
FILE: src/Profiler/Profile/Code.php
================================================
<?php
namespace Mirasvit\Profiler\Profile;
use Mirasvit\Profiler\Api\Data\ProfileInterface;
class Code implements ProfileInterface
{
/**
* {@inheritdoc}
*/
public function dump()
{
$dump = [];
foreach ($this->getStat()->getFilteredTimerIds() as $timerId) {
$dump[$timerId] = $this->getStat()->get($timerId);
}
return $dump;
}
/**
* @return \Magento\Framework\Profiler\Driver\Standard\Stat
*/
private function getStat()
{
return $_SERVER['MAGE_PROFILER_STAT'];
}
}
================================================
FILE: src/Profiler/Profile/Context.php
================================================
<?php
namespace Mirasvit\Profiler\Profile;
class Context
{
/**
* @return float
*/
public function getExecutionTime()
{
return (microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000;
}
/**
* @return string|false
*/
public function getClientIP()
{
if (isset($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
}
return false;
}
/**
* @return string|false
*/
public function getURI()
{
if (isset($_SERVER['REQUEST_URI'])) {
return $_SERVER['REQUEST_URI'];
}
return false;
}
/**
* @return bool
*/
public function isCLI()
{
return PHP_SAPI == 'cli';
}
/**
* @return false|string
*/
public function getCliArgs()
{
if ($this->isCLI()) {
global $argv;
return implode(' ', $argv);
}
return false;
}
}
================================================
FILE: src/Profiler/Profile/Database.php
================================================
<?php
namespace Mirasvit\Profiler\Profile;
use Magento\Framework\App\ResourceConnection;
use Mirasvit\Profiler\Api\Data\ProfileInterface;
class Database implements ProfileInterface
{
const TOTAL_ELAPSED = 'totalElapsedSecs';
const TOTAL_QUERIES = 'totalNumQueries';
const QUERY = 'query';
const QUERY_TYPE = 'queryType';
const QUERY_PARAMS = 'queryParams';
const QUERY_ELAPSED = 'elapsedSecs';
const QUERY_STARTED = 'startedMicrotime';
const QUERY_COUNT = 'queryCount';
/**
* @var ResourceConnection
*/
private $resourceConnection;
public function __construct(
ResourceConnection $resourceConnection
) {
$this->resourceConnection = $resourceConnection;
}
/**
* {@inheritdoc}
*/
public function dump()
{
$queryCountBucket = [];
$dump = [
self::TOTAL_ELAPSED => $this->getProfiler()->getTotalElapsedSecs() * 1000,
self::TOTAL_QUERIES => $this->getProfiler()->getTotalNumQueries(),
'profiles' => [],
];
$profiles = $this->getProfiler()->getQueryProfiles();
if (is_array($profiles)) {
/** @var \Zend_Db_Profiler_Query $profile */
foreach ($profiles as $profile) {
if (!isset($queryCountBucket[$profile->getQuery()])) {
$queryCountBucket[$profile->getQuery()] = 0;
}
$queryCountBucket[$profile->getQuery()]++;
$dump['profiles'][] = [
self::QUERY => $profile->getQuery(),
self::QUERY_TYPE => $profile->getQueryType(),
self::QUERY_PARAMS => $profile->getQueryParams(),
self::QUERY_ELAPSED => $profile->getElapsedSecs() * 1000,
self::QUERY_STARTED => $profile->getStartedMicrotime(),
self::QUERY_COUNT => $queryCountBucket[$profile->getQuery()]
];
}
}
return $dump;
}
/**
* @return \Zend_Db_Profiler
*/
private function getProfiler()
{
return $this->resourceConnection->getConnection('read')
->getProfiler();
}
}
================================================
FILE: src/Profiler/Profile/General.php
================================================
<?php
namespace Mirasvit\Profiler\Profile;
use Mirasvit\Profiler\Api\Data\ProfileInterface;
use Magento\Framework\App\ResourceConnection;
class General implements ProfileInterface
{
const EXECUTION_TIME = 'EXECUTION_TIME';
const IP = 'IP';
const URI = 'URI';
const IS_CLI = 'IS_CLI';
const CLI_ARGS = 'CLI_ARGS';
const DB_QUERIES = 'DB_QUERIES';
const DB_TIME = 'DB_TIME';
const POST = 'POST';
const GET = 'GET';
/**
* @var ResourceConnection
*/
private $resourceConnection;
/**
* @var Context
*/
private $context;
public function __construct(
ResourceConnection $resourceConnection,
Context $context
) {
$this->resourceConnection = $resourceConnection;
$this->context = $context;
}
/**
* {@inheritdoc}
*/
public function dump()
{
/** @var \Zend_Db_Profiler $db */
$db = $this->resourceConnection->getConnection('read')
->getProfiler();
$dump = [
self::EXECUTION_TIME => $this->context->getExecutionTime(),
self::IP => $this->context->getClientIP(),
self::URI => $this->context->getURI(),
self::IS_CLI => $this->context->isCLI(),
self::CLI_ARGS => $this->context->getCliArgs(),
self::DB_QUERIES => $db->getTotalNumQueries(),
self::DB_TIME => round($db->getTotalElapsedSecs() * 1000),
self::GET => isset($_GET) ? $_GET : [],
self::POST => isset($_POST) ? $_POST : [],
];
return $dump;
}
}
================================================
FILE: src/Profiler/Profile/Meta.php
================================================
<?php
namespace Mirasvit\Profiler\Profile;
use Mirasvit\Profiler\Api\Data\ProfileInterface;
class Meta implements ProfileInterface
{
const RESPONSE_CODE = 'RESPONSE_CODE';
const IP = 'IP';
const METHOD = 'METHOD';
const TIME = 'TIME';
const EXECUTION_TIME = 'EXECUTION_TIME';
const URL = 'URL';
private $context;
public function __construct(
Context $context
) {
$this->context = $context;
}
/**
* {@inheritdoc}
*/
public function dump()
{
if ($this->context->isCLI()) {
$url = $this->context->getCliArgs();
} else {
$url = $this->context->getURI();
}
$dump = [
'RESPONSE_CODE' => http_response_code(),
'METHOD' => $this->context->isCLI() ? 'CLI' : $_SERVER['REQUEST_METHOD'],
'TIME' => microtime(true),
'EXECUTION_TIME' => $this->context->getExecutionTime(),
'URL' => $url,
'IP' => $this->context->getClientIP(),
];
return $dump;
}
}
================================================
FILE: src/Profiler/Profile/Pool.php
================================================
<?php
namespace Mirasvit\Profiler\Profile;
use Mirasvit\Profiler\Api\Data\ProfileInterface;
class Pool
{
/**
* @var ProfileInterface[]
*/
private $profiles;
public function __construct(
$profiles = []
) {
$this->profiles = $profiles;
}
/**
* @return ProfileInterface[]
*/
public function getProfiles()
{
return $this->profiles;
}
}
================================================
FILE: src/Profiler/etc/di.xml
================================================
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Framework\Console\CommandList">
<arguments>
<argument name="commands" xsi:type="array">
<item name="ProfilerEnableCommand" xsi:type="object">Mirasvit\Profiler\Console\Command\EnableCommand</item>
<item name="ProfilerDisableCommand" xsi:type="object">Mirasvit\Profiler\Console\Command\DisableCommand</item>
<item name="ProfilerAllowIpsCommand" xsi:type="object">Mirasvit\Profiler\Console\Command\AllowIpsCommand</item>
<item name="ProfilerStatusCommand" xsi:type="object">Mirasvit\Profiler\Console\Command\StatusCommand</item>
</argument>
</arguments>
</type>
<type name="Mirasvit\Profiler\Block\Profile\View">
<arguments>
<argument name="tabs" xsi:type="array">
<item name="ioTab" xsi:type="object">Mirasvit\Profiler\Block\Tab\IO</item>
<item name="codeTab" xsi:type="object">Mirasvit\Profiler\Block\Tab\Code</item>
<item name="databaseTab" xsi:type="object">Mirasvit\Profiler\Block\Tab\Database</item>
</argument>
</arguments>
</type>
<type name="Mirasvit\Profiler\Profile\Pool">
<arguments>
<argument name="profiles" xsi:type="array">
<item name="general" xsi:type="object">Mirasvit\Profiler\Profile\General</item>
<item name="meta" xsi:type="object">Mirasvit\Profiler\Profile\Meta</item>
<item name="code" xsi:type="object">Mirasvit\Profiler\Profile\Code</item>
<item name="database" xsi:type="object">Mirasvit\Profiler\Profile\Database</item>
</argument>
</arguments>
</type>
</config>
================================================
FILE: src/Profiler/etc/frontend/routes.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route id="profiler" frontName="profiler">
<module name="Mirasvit_Profiler"/>
</route>
</router>
</config>
================================================
FILE: src/Profiler/etc/module.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">
<module name="Mirasvit_Profiler" setup_version="1.0.0"/>
</config>
================================================
FILE: src/Profiler/view/base/layout/profiler_profile_index.xml
================================================
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="profiler"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"
label="Profiler">
<body>
<referenceContainer name="profiler">
<block class="Mirasvit\Profiler\Block\Profile\Listing" template="Mirasvit_Profiler::profile/listing.phtml"
cacheable="false"/>
</referenceContainer>
</body>
</page>
================================================
FILE: src/Profiler/view/base/layout/profiler_profile_view.xml
================================================
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="profiler"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"
label="Profiler">
<body>
<referenceContainer name="profiler">
<block class="Mirasvit\Profiler\Block\Profile\View" template="Mirasvit_Profiler::profile/view.phtml"
cacheable="false"/>
</referenceContainer>
</body>
</page>
================================================
FILE: src/Profiler/view/base/page_layout/profiler.xml
================================================
<?xml version="1.0"?>
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_layout.xsd">
<container name="profiler">
<!--<container name="main" as="main" after="-" label="main"/>-->
</container>
</layout>
================================================
FILE: src/Profiler/view/base/templates/profile/listing.phtml
================================================
<?php
/** @var \Mirasvit\Profiler\Block\Profile\Listing $block */
use Mirasvit\Profiler\Profile\Meta;
/** @var \Mirasvit\Profiler\Helper\Format $format */
$format = $this->helper('Mirasvit\Profiler\Helper\Format');
?>
<table id="listing-table">
<thead>
<tr>
<th></th>
<th>Status</th>
<th>Method</th>
<th>IP</th>
<th>Time</th>
<th>Execution <sup>ms</sup></th>
<th>URL</th>
</tr>
</thead>
<tbody>
<?php foreach ($block->getList() as $item): ?>
<tr>
<td class="text-center">
<a href="<?= $block->getUrl('profiler/profile/view', ['id' => $item['ID']]) ?>">
view
</a>
</td>
<td class="text-center">
<div class="badge code-<?= $item[Meta::RESPONSE_CODE] ?>">
<?= $item[Meta::RESPONSE_CODE] ?>
</div>
</td>
<td class="text-center"><?= $item[Meta::METHOD] ?></td>
<td><?= $item[Meta::IP] ?></td>
<td nowrap="nowrap"><?= date('M d, H:i:s', $item[Meta::TIME]) ?></td>
<td nowrap="nowrap" class="text-right"><?= $format->formatTime($item[Meta::EXECUTION_TIME]) ?></td>
<td><?= $item[Meta::URL] ?></td>
</tr>
<?php endforeach ?>
</tbody>
</table>
<script>
$(function () {
$("#listing-table").tablesorter();
});
</script>
================================================
FILE: src/Profiler/view/base/templates/profile/view.phtml
================================================
<?php
use Mirasvit\Profiler\Profile\Meta;
use Mirasvit\Profiler\Profile\General;
/** @var \Mirasvit\Profiler\Block\Profile\View $block */
$profile = $block->getProfile();
$general = $profile['general'];
?>
<div class="tabs-container">
<ul class="tabs" data-role="tabs">
<?php foreach ($block->getTabs() as $key => $tab): ?><li data-tab="<?= $key ?>">
<i class="fa fa-fw fa-<?=$tab->getIcon() ?>"></i> <?= $tab->getLabel() ?>
</li><?php endforeach ?>
<li class="link" style="float: right;">
<i class="fa fa-chevron-left"></i>
<a href="<?=$block->getUrl('profiler/profile/index') ?>">Back to list</a>
</li>
</ul>
<?php foreach ($block->getTabs() as $key => $tab): ?>
<div class="tab-content" data-tab-content="<?= $key ?>">
<?= $tab->toHtml() ?>
</div>
<?php endforeach ?>
</div>
<script>
$(function () {
$('[data-role=tabs] li').on('click', function (e) {
if ($(e.currentTarget).attr('data-tab')) {
$('[data-role=tabs] li').removeClass('_active');
$(e.currentTarget).addClass('_active');
$('[data-tab-content]').hide();
$('[data-tab-content=' + $(e.currentTarget).attr('data-tab') + ']').show();
}
});
$('[data-role=tabs] li:first').click();
});
</script>
================================================
FILE: src/Profiler/view/base/templates/root.phtml
================================================
<!doctype html>
<html>
<head>
<script src="//code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.28.15/js/jquery.tablesorter.min.js"></script>
<style>
<?=file_get_contents(dirname(dirname(__FILE__)) . '/web/css/module.css') ?>
</style>
</head>
<body>
<?= $layoutContent ?>
<script>
$(function () {
setInterval(function () {
parent.postMessage($('body').height(), '*');
}, 1000);
})
</script>
</body>
</html>
================================================
FILE: src/Profiler/view/base/templates/tab/code.phtml
================================================
<?php
/** @var \Mirasvit\Profiler\Block\Tab\Code $block */
use Mirasvit\Profiler\Profile\General;
$general = $block->getGeneralDump();
/** @var \Mirasvit\Profiler\Helper\Format $format */
$format = $this->helper(\Mirasvit\Profiler\Helper\Format::class);
?>
<div class="metric">
<strong><?= $format->formatTime($general[General::EXECUTION_TIME]) ?> ms</strong>
<i>Total Time</i>
</div>
<div class="metric">
<strong><?= $format->formatTime($general[General::DB_TIME]) ?> ms</strong>
<i>Database Time</i>
</div>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@1.0.11/dist/d3.flameGraph.min.css">
<div id="chart"></div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.7.1/d3-tip.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/spiermar/d3-flame-graph@1.0.11/dist/d3.flameGraph.min.js"></script>
<script type="text/javascript">
const flameGraphJson = <?= $block->getFlameGraphJson(); ?>;
const flamegraph = d3.flameGraph()
.width(1400)
// .height(1080)
// .cellHeight(18)
.transitionEase(d3.easeCubic)
.sort(false)
.title("")
.reversed(true);
const tip = d3.tip()
.direction("s")
.offset([8, 0])
.attr('class', 'd3-flame-graph-tip')
.html((d) => {
const ms = Math.round((d.data.sum/d.data.count * 1000));
console.log(d.data);
const memory = d.data.realmem ? `Memory: ${formatBytes(d.data.realmem)}` : '';
return `${d.data.name}<br />${d.data.count}x${ms}ms ${memory}`;
});
function formatBytes(bytes,decimals) {
if(bytes == 0) return '0 Bytes';
var k = 1024,
dm = decimals || 2,
sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
flamegraph.tooltip(tip);
d3.select("#chart")
.datum(flameGraphJson)
.call(flamegraph);
</script>
================================================
FILE: src/Profiler/view/base/templates/tab/database.phtml
================================================
<?php
/** @var \Mirasvit\Profiler\Block\Tab\Database $block */
use Mirasvit\Profiler\Profile\Database;
$dump = $block->getDump();
/** @var \Mirasvit\Profiler\Helper\Format $format */
$format = $this->helper('Mirasvit\Profiler\Helper\Format');
?>
<div class="metric">
<strong><?= $format->formatTime($dump[Database::TOTAL_ELAPSED]) ?> ms</strong>
<i>Total Queries Time</i>
</div>
<div class="metric">
<strong><?= $dump[Database::TOTAL_QUERIES] ?></strong>
<i>Total Queries</i>
</div>
<div class="mst-threshold" id="sql-table-threshold">
<div class="value"></div>
</div>
<?php $idx = 0 ?>
<table id="sql-table" data-threshold="#sql-table-threshold">
<thead>
<tr>
<th>#</th>
<th>Query Occurance</th>
<th>Time <sup>ms</sup></th>
<th>SQL Query</th>
<th>Query Params</th>
</tr>
</thead>
<tbody>
<?php foreach ($dump['profiles'] as $query): ?>
<tr data-threshold-value="<?= $query[Database::QUERY_ELAPSED] ?>">
<td class="text-right"><?= ++$idx ?></td>
<td><?= $query[Database::QUERY_COUNT] ?? 'N/A' ?></td>
<td class="text-right">
<?= $format->formatTime($query[Database::QUERY_ELAPSED]) ?>
</td>
<td><?= \SqlFormatter::format($query[Database::QUERY]) ?></td>
<td><?= $format->any($query[Database::QUERY_PARAMS]) ?></td>
</tr>
<?php endforeach ?>
</tbody>
</table>
<script>
$(function () {
$("#sql-table").tablesorter();
});
</script>
================================================
FILE: src/Profiler/view/base/templates/tab/io.phtml
================================================
<?php
/** @var \Mirasvit\Profiler\Block\Tab\IO $block */
use Magento\Framework\Profiler\Driver\Standard\Stat;
use Mirasvit\Profiler\Profile\General;
$dump = $block->getDump();
$general = $dump['general'];
/** @var \Mirasvit\Profiler\Helper\Format $format */
$format = $this->helper('Mirasvit\Profiler\Helper\Format');
?>
<table>
<tr>
<td>URL</td>
<td><?= $general[General::URI] ?></td>
</tr>
<tr>
<td>GET Params</td>
<td><?= $format->any($general[General::GET]) ?></td>
</tr>
<tr>
<td>POST Params</td>
<td><?= $format->any($general[General::POST]) ?></td>
</tr>
<tr>
<td>IP</td>
<td><?= $general[General::IP] ?></td>
</tr>
</table>
================================================
FILE: src/Profiler/view/base/templates/toolbar.phtml
================================================
<?php
/** @var \Mirasvit\Profiler\Block\Toolbar $block */
use Mirasvit\Profiler\Profile\Meta;
use Mirasvit\Profiler\Profile\Database;
$dump = $block->getDump();
$meta = $dump['meta'];
$database = $dump['database'];
?>
<div class="mst-profiler__toolbar">
<section>
<?= $meta[Meta::RESPONSE_CODE] ?>
</section>
<section>
<i class="fa fa-clock-o"></i> <?= round($meta[Meta::EXECUTION_TIME]) ?> <i>ms</i>
</section>
<section>
<i class="fa fa-database"></i> <?= $database[Database::TOTAL_QUERIES] ?>
<i>in</i> <?= round($database[Database::TOTAL_ELAPSED]) ?> <i>ms</i>
</section>
<section class="view">
<a href="<?= $block->getProfileUrl() ?>" target="_blank">View</a>
</section>
</div>
<style>
.mst-profiler__toolbar {
width: 100%;
background: #373330;
position: fixed;
bottom: 0;
left: 0;
z-index: 100000;
color: #fff;
font-size: 13px;
padding: 0;
margin: 0;
box-shadow: 0 -1px 1px rgba(255, 255, 255, 0.3);
}
.mst-profiler__toolbar section {
display: inline-block;
padding: 8px 10px;
margin: 0;
border-right: 1px solid #524e4b;
}
.mst-profiler__toolbar section i {
font-style: normal;
color: #bbb;
}
.mst-profiler__toolbar section i.fa {
margin-right: 5px;
}
.mst-profiler__toolbar section.view {
background: #524e4b;
float: right;
}
.mst-profiler__toolbar section.view a {
color: #fff;
text-decoration: underline;
padding-left: 15px;
padding-right: 15px;
}
</style>
================================================
FILE: src/Profiler/view/base/web/css/less/_module/_base.less
================================================
html, body {
margin: 0;
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-style: normal;
font-weight: 400;
font-size: 13px;
color: @text-color;
}
a {
color: @link-color;
&:hover {
text-decoration: none;
}
}
pre {
background: transparent !important;
margin: 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
border: none;
tr {
th, td {
padding: 10px;
}
th {
background-color: @bg-dark;
border: 1px solid @border-dark;
color: #ffffff;
font-weight: 600;
white-space: nowrap;
&:first-child {
border-left: 0;
}
&:last-child {
border-right: 0;
}
}
td {
background-color: #ffffff;
border-left: 1px dashed @border-light;
border-right: 1px dashed @border-light;
vertical-align: top;
&:first-child {
border-left: 0;
}
&:last-child {
border-right: 0;
}
}
&:nth-child(even) {
td {
background: @bg-light;
}
}
&:last-child {
td {
border-bottom: 1px solid @border-light;
}
}
&:hover {
td {
background: #e5f7fe;
}
}
}
}
.tablesorter {
.tablesorter-header {
text-decoration: underline;
&.tablesorter-headerDesc, &.tablesorter-headerAsc {
background: lighten(@bg-dark, 20%);
}
sup {
text-decoration: none;
}
}
}
.text-left {
text-align: left;
}
.text-right {
text-align: right;
}
.text-center {
text-align: center;
}
================================================
FILE: src/Profiler/view/base/web/css/less/_module/_etc.less
================================================
.metric {
border: 1px solid @border-light;
display: inline-block;
margin: 10px 10px 10px 0;
strong {
text-align: center;
font-size: 25px;
font-weight: 300;
display: block;
padding: 10px;
}
i {
display: block;
background: @border-light;
font-style: normal;
text-align: center;
padding: 3px 10px;
font-size: 12px;
font-weight: 600;
}
}
.badge {
border-radius: 1px;
background: #eee;
&.code-200 {
background: #00c602;
color: #fff;
}
&.code-301, &.code-302 {
background: #eebe00;
color: #fff;
}
&.code-404, &.code-503, &.code-500 {
background: #c6000c;
color: #fff;
}
}
================================================
FILE: src/Profiler/view/base/web/css/less/_module/_tabs.less
================================================
.tabs-container {
.tabs {
list-style: none;
padding: 0 10px 0 10px;
margin: 0;
background: @bg-dark;
li {
display: inline-block;
background: @bg-dark;
color: #fff;
font-weight: 600;
padding: 10px;
cursor: pointer;
border-bottom: 3px solid transparent;
a {
color: #fff;
}
&:hover {
border-bottom: 3px solid @orange;
}
&._active {
background: @bg-dark-hover;
border-bottom: 3px solid @orange;
}
&.link {
border-bottom: 3px solid transparent !important;
}
}
}
.tab-content {
width: 100%;
display: none;
padding: 10px;
box-sizing: border-box;
}
}
================================================
FILE: src/Profiler/view/base/web/css/less/_module.less
================================================
@import url('//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css');
@text-color: #303030;
@link-color: #278CBA;
@bg-light: #f5f5f5;
@bg-dark: #514943;
@bg-dark-hover: lighten(#514943, 10%);
@border-dark: #8a837f;
@border-light: #d6d6d6;
@orange: #EE5100;
@import "_module/_base.less";
@import "_module/_tabs.less";
@import "_module/_etc.less";
================================================
FILE: src/Profiler/view/base/web/css/module.css
================================================
@import url('//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css');
html,
body {
margin: 0;
font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-style: normal;
font-weight: 400;
font-size: 13px;
color: #303030;
}
a {
color: #278cba;
}
a:hover {
text-decoration: none;
}
pre {
background: transparent !important;
margin: 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
border: none;
}
table tr th,
table tr td {
padding: 10px;
}
table tr th {
background-color: #514943;
border: 1px solid #8a837f;
color: #ffffff;
font-weight: 600;
white-space: nowrap;
}
table tr th:first-child {
border-left: 0;
}
table tr th:last-child {
border-right: 0;
}
table tr td {
background-color: #ffffff;
border-left: 1px dashed #d6d6d6;
border-right: 1px dashed #d6d6d6;
vertical-align: top;
}
table tr td:first-child {
border-left: 0;
}
table tr td:last-child {
border-right: 0;
}
table tr:nth-child(even) td {
background: #f5f5f5;
}
table tr:last-child td {
border-bottom: 1px solid #d6d6d6;
}
table tr:hover td {
background: #e5f7fe;
}
.tablesorter .tablesorter-header {
text-decoration: underline;
}
.tablesorter .tablesorter-header.tablesorter-headerDesc,
.tablesorter .tablesorter-header.tablesorter-headerAsc {
background: #897b71;
}
.tablesorter .tablesorter-header sup {
text-decoration: none;
}
.text-left {
text-align: left;
}
.text-right {
text-align: right;
}
.text-center {
text-align: center;
}
.tabs-container .tabs {
list-style: none;
padding: 0 10px 0 10px;
margin: 0;
background: #514943;
}
.tabs-container .tabs li {
display: inline-block;
background: #514943;
color: #fff;
font-weight: 600;
padding: 10px;
cursor: pointer;
border-bottom: 3px solid transparent;
}
.tabs-container .tabs li a {
color: #fff;
}
.tabs-container .tabs li:hover {
border-bottom: 3px solid #ee5100;
}
.tabs-container .tabs li._active {
background: #6d625a;
border-bottom: 3px solid #ee5100;
}
.tabs-container .tabs li.link {
border-bottom: 3px solid transparent !important;
}
.tabs-container .tab-content {
width: 100%;
display: none;
padding: 10px;
box-sizing: border-box;
}
.metric {
border: 1px solid #d6d6d6;
display: inline-block;
margin: 10px 10px 10px 0;
}
.metric strong {
text-align: center;
font-size: 25px;
font-weight: 300;
display: block;
padding: 10px;
}
.metric i {
display: block;
background: #d6d6d6;
font-style: normal;
text-align: center;
padding: 3px 10px;
font-size: 12px;
font-weight: 600;
}
.badge {
border-radius: 1px;
background: #eee;
}
.badge.code-200 {
background: #00c602;
color: #fff;
}
.badge.code-301,
.badge.code-302 {
background: #eebe00;
color: #fff;
}
.badge.code-404,
.badge.code-503,
.badge.code-500 {
background: #c6000c;
color: #fff;
}
================================================
FILE: src/Profiler/view/base/web/js/lib/jquery.filtertable.js
================================================
/**
* jquery.filterTable
*
* This plugin will add a search filter to tables. When typing in the filter,
* any rows that do not contain the filter will be hidden.
*
* Utilizes bindWithDelay() if available. https://github.com/bgrins/bindWithDelay
*
* @version v1.5.5
* @author Sunny Walker, swalker@hawaii.edu
* @license MIT
*/
(function ($) {
var jversion = $.fn.jquery.split('.'),
jmajor = parseFloat(jversion[0]),
jminor = parseFloat(jversion[1]);
if (jmajor < 2 && jminor < 8) { // build the pseudo selector for jQuery < 1.8
$.expr[':'].filterTableFind = function (a, i, m) { // build the case insensitive filtering functionality as a pseudo-selector expression
return $(a).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
};
$.expr[':'].filterTableFindAny = function (a, i, m) { // build the case insensitive all-words filtering functionality as a pseudo-selector expression
// build an array of each non-falsey value passed
var raw_args = m[3].split(/[\s,]/),
args = [];
$.each(raw_args, function (j, v) {
var t = v.replace(/^\s+|\s$/g, '');
if (t) {
args.push(t);
}
});
// if there aren't any non-falsey values to search for, abort
if (!args.length) {
return false;
}
return function (a) {
var found = false;
$.each(args, function (j, v) {
if ($(a).text().toUpperCase().indexOf(v.toUpperCase()) >= 0) {
found = true;
return false;
}
});
return found;
};
};
$.expr[':'].filterTableFindAll = function (a, i, m) { // build the case insensitive all-words filtering functionality as a pseudo-selector expression
// build an array of each non-falsey value passed
var raw_args = m[3].split(/[\s,]/),
args = [];
$.each(raw_args, function (j, v) {
var t = v.replace(/^\s+|\s$/g, '');
if (t) {
args.push(t);
}
});
// if there aren't any non-falsey values to search for, abort
if (!args.length) {
return false;
}
return function (a) {
var found = 0; // how many terms were found?
$.each(args, function (j, v) {
if ($(a).text().toUpperCase().indexOf(v.toUpperCase()) >= 0) {
found++; // found another term
}
});
return found === args.length; // did we find all of them in this cell?
};
};
} else { // build the pseudo selector for jQuery >= 1.8
$.expr[':'].filterTableFind = jQuery.expr.createPseudo(function (arg) {
return function (el) {
return $(el).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
};
});
$.expr[':'].filterTableFindAny = jQuery.expr.createPseudo(function (arg) {
// build an array of each non-falsey value passed
var raw_args = arg.split(/[\s,]/),
args = [];
$.each(raw_args, function (i, v) {
var t = v.replace(/^\s+|\s$/g, ''); // trim the string
if (t) {
args.push(t);
}
});
// if there aren't any non-falsey values to search for, abort
if (!args.length) {
return false;
}
return function (el) {
var found = false;
$.each(args, function (i, v) {
if ($(el).text().toUpperCase().indexOf(v.toUpperCase()) >= 0) {
found = true;
return false; // short-circuit the searching since this cell has one of the terms
}
});
return found;
};
});
$.expr[':'].filterTableFindAll = jQuery.expr.createPseudo(function (arg) {
// build an array of each non-falsey value passed
var raw_args = arg.split(/[\s,]/),
args = [];
$.each(raw_args, function (i, v) {
var t = v.replace(/^\s+|\s$/g, ''); // trim the string
if (t) {
args.push(t);
}
});
// if there aren't any non-falsey values to search for, abort
if (!args.length) {
return false;
}
return function (el) {
var found = 0; // how many terms were found?
$.each(args, function (i, v) {
if ($(el).text().toUpperCase().indexOf(v.toUpperCase()) >= 0) {
found++; // found another term
}
});
return found === args.length; // did we find all of them in this cell?
};
});
}
$.fn.filterTable = function (options) { // define the filterTable plugin
var defaults = { // start off with some default settings
autofocus: false, // make the filter input field autofocused (not recommended for accessibility)
callback: null, // callback function: function(term, table){}
containerClass: 'filter-table', // class to apply to the container
containerTag: 'p', // tag name of the container
filterExpression: 'filterTableFind', // jQuery expression method to use for filtering
hideTFootOnFilter: false, // if true, the table's tfoot(s) will be hidden when the table is filtered
highlightClass: 'alt', // class applied to cells containing the filter term
ignoreClass: '', // don't filter the contents of cells with this class
ignoreColumns: [], // don't filter the contents of these columns
inputSelector: null, // use the element with this selector for the filter input field instead of creating one
inputName: '', // name of filter input field
inputType: 'search', // tag name of the filter input tag
label: '', // text to precede the filter input tag
minChars: 1, // filter only when at least this number of characters are in the filter input field
minRows: 8, // don't show the filter on tables with at least this number of rows
placeholder: 'search this table', // HTML5 placeholder text for the filter field
preventReturnKey: true, // prevent the return key in the filter input field from trigger form submits
quickList: [], // list of phrases to quick fill the search
quickListClass: 'quick', // class of each quick list item
quickListGroupTag: '', // tag surrounding quick list items (e.g., ul)
quickListTag: 'a', // tag type of each quick list item (e.g., a or li)
visibleClass: 'visible' // class applied to visible rows
},
hsc = function (text) { // mimic PHP's htmlspecialchars() function
return text.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
},
settings = $.extend({}, defaults, options); // merge the user's settings into the defaults
var doFiltering = function (table, q) { // handle the actual table filtering
var tbody = table.find('tbody'); // cache the tbody element
if (q === '' || q.length < settings.minChars) { // if the filtering query is blank or the number of chars is less than the minChars option
tbody.find('tr').show().addClass(settings.visibleClass); // show all rows
tbody.find('td').removeClass(settings.highlightClass); // remove the row highlight from all cells
if (settings.hideTFootOnFilter) { // show footer if the setting was specified
table.find('tfoot').show();
}
} else { // if the filter query is not blank
var all_tds = tbody.find('td');
tbody.find('tr').hide().removeClass(settings.visibleClass); // hide all rows, assuming none were found
all_tds.removeClass(settings.highlightClass); // remove previous highlights
if (settings.hideTFootOnFilter) { // hide footer if the setting was specified
table.find('tfoot').hide();
}
if (settings.ignoreColumns.length) {
var tds = [];
if (settings.ignoreClass) {
all_tds = all_tds.not('.' + settings.ignoreClass);
}
tds = all_tds.filter(':' + settings.filterExpression + '("' + q.replace(/(['"])/g, '\\$1') + '")');
tds.each(function () {
var t = $(this),
col = t.parent().children().index(t);
//window.console.log(t.text(), col);
if ($.inArray(col, settings.ignoreColumns) === -1) {
//window.console.log(t.text(), $.inArray(col, settings.ignoreColumns));
t.addClass(settings.highlightClass).closest('tr').show().addClass(settings.visibleClass);
}
});
} else {
if (settings.ignoreClass) {
all_tds = all_tds.not('.' + settings.ignoreClass);
}
all_tds.filter(':' + settings.filterExpression + '("' + q.replace(/(['"])/g, '\\$1') + '")').addClass(settings.highlightClass).closest('tr').show().addClass(settings.visibleClass); // highlight (class=alt) only the cells that match the query and show their rows
}
}
if (settings.callback) { // call the callback function
settings.callback(q, table);
}
}; // doFiltering()
return this.each(function () {
var t = $(this), // cache the table
tbody = t.find('tbody'), // cache the tbody
container = null, // placeholder for the filter field container DOM node
quicks = null, // placeholder for the quick list items
filter = null, // placeholder for the field field DOM node
created_filter = true; // was the filter created or chosen from an existing element?
if (t[0].nodeName === 'TABLE' && tbody.length > 0 && (settings.minRows === 0 || (settings.minRows > 0 && tbody.find('tr').length >= settings.minRows)) && !t.prev().hasClass(settings.containerClass)) { // only if object is a table and there's a tbody and at least minRows trs and hasn't already had a filter added
if (settings.inputSelector && $(settings.inputSelector).length === 1) { // use a single existing field as the filter input field
filter = $(settings.inputSelector);
container = filter.parent(); // container to hold the quick list options
created_filter = false;
} else { // create the filter input field (and container)
container = $('<' + settings.containerTag + ' />'); // build the container tag for the filter field
if (settings.containerClass !== '') { // add any classes that need to be added
container.addClass(settings.containerClass);
}
container.prepend(settings.label + ' '); // add the label for the filter field
filter = $('<input type="' + settings.inputType + '" placeholder="' + settings.placeholder + '" name="' + settings.inputName + '" />'); // build the filter field
if (settings.preventReturnKey) { // prevent return in the filter field from submitting any forms
filter.on('keydown', function (ev) {
if ((ev.keyCode || ev.which) === 13) {
ev.preventDefault();
return false;
}
});
}
}
if (settings.autofocus) { // add the autofocus attribute if requested
filter.attr('autofocus', true);
}
if ($.fn.bindWithDelay) { // does bindWithDelay() exist?
filter.bindWithDelay('keyup', function () { // bind doFiltering() to keyup (delayed)
doFiltering(t, $(this).val());
}, 200);
} else { // just bind to onKeyUp
filter.bind('keyup', function () { // bind doFiltering() to keyup
doFiltering(t, $(this).val());
});
} // keyup binding block
filter.bind('click search input paste blur', function () { // bind doFiltering() to additional events
doFiltering(t, $(this).val());
});
if (created_filter) { // add the filter field to the container if it was created by the plugin
container.append(filter);
}
if (settings.quickList.length > 0) { // are there any quick list items to add?
quicks = settings.quickListGroupTag ? $('<' + settings.quickListGroupTag + ' />') : container;
$.each(settings.quickList, function (index, value) { // for each quick list item...
var q = $('<' + settings.quickListTag + ' class="' + settings.quickListClass + '" />'); // build the quick list item link
q.text(hsc(value)); // add the item's text
if (q[0].nodeName === 'A') {
q.attr('href', '#'); // add a (worthless) href to the item if it's an anchor tag so that it gets the browser's link treatment
}
q.bind('click', function (e) { // bind the click event to it
e.preventDefault(); // stop the normal anchor tag behavior from happening
filter.val(value).focus().trigger('click'); // send the quick list value over to the filter field and trigger the event
});
quicks.append(q); // add the quick list link to the quick list groups container
}); // each quick list item
if (quicks !== container) {
container.append(quicks); // add the quick list groups container to the DOM if it isn't already there
}
} // if quick list items
if (created_filter) { // add the filter field and quick list container to just before the table if it was created by the plugin
t.before(container);
}
} // if the functionality should be added
}); // return this.each
}; // $.fn.filterTable
})(jQuery);
================================================
FILE: src/Profiler/view/base/web/js/lib/jquery.tablesorter.js
================================================
/*
*
* TableSorter 2.0 - Client-side table sorting with ease!
* Version 2.0.5b
* @requires jQuery v1.2.3
*
* Copyright (c) 2007 Christian Bach
* Examples and docs at: http://tablesorter.com
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
/**
*
* @description Create a sortable table with multi-column sorting capabilitys
*
* @example $('table').tablesorter();
* @desc Create a simple tablesorter interface.
*
* @example $('table').tablesorter({ sortList:[[0,0],[1,0]] });
* @desc Create a tablesorter interface and sort on the first and secound column column headers.
*
* @example $('table').tablesorter({ headers: { 0: { sorter: false}, 1: {sorter: false} } });
*
* @desc Create a tablesorter interface and disableing the first and second column headers.
*
*
* @example $('table').tablesorter({ headers: { 0: {sorter:"integer"}, 1: {sorter:"currency"} } });
*
* @desc Create a tablesorter interface and set a column parser for the first
* and second column.
*
*
* @param Object
* settings An object literal containing key/value pairs to provide
* optional settings.
*
*
* @option String cssHeader (optional) A string of the class name to be appended
* to sortable tr elements in the thead of the table. Default value:
* "header"
*
* @option String cssAsc (optional) A string of the class name to be appended to
* sortable tr elements in the thead on a ascending sort. Default value:
* "headerSortUp"
*
* @option String cssDesc (optional) A string of the class name to be appended
* to sortable tr elements in the thead on a descending sort. Default
* value: "headerSortDown"
*
* @option String sortInitialOrder (optional) A string of the inital sorting
* order can be asc or desc. Default value: "asc"
*
* @option String sortMultisortKey (optional) A string of the multi-column sort
* key. Default value: "shiftKey"
*
* @option String textExtraction (optional) A string of the text-extraction
* method to use. For complex html structures inside td cell set this
* option to "complex", on large tables the complex option can be slow.
* Default value: "simple"
*
* @option Object headers (optional) An object of instructions for per-column
* controls in the format: headers: { 0: { option: setting }, ... }. For
* example, to disable sorting on the first two columns of a table:
* headers: { 0: { sorter: false}, 1: {sorter: false} }.
* Default value: null.
*
* @option Array sortList (optional) An array of instructions for per-column sorting
* and direction in the format: [[columnIndex, sortDirection], ... ] where
* columnIndex is a zero-based index for your columns left-to-right and
* sortDirection is 0 for Ascending and 1 for Descending. A valid argument
* that sorts ascending first by column 1 and then column 2 looks like:
* [[0,0],[1,0]]. Default value: null.
*
* @option Array sortForce (optional) An array containing forced sorting rules.
* Use to add an additional forced sort that will be appended to the dynamic
* selections by the user. For example, can be used to sort people alphabetically
* after some other user-selected sort that results in rows with the same value
* like dates or money due. It can help prevent data from appearing as though it
* has a random secondary sort. Default value: null.
*
* @option Boolean sortLocaleCompare (optional) Boolean flag indicating whatever
* to use String.localeCampare method or not. Default set to true.
*
*
* @option Array sortAppend (optional) An array containing forced sorting rules.
* This option let's you specify a default sorting rule, which is
* appended to user-selected rules. Default value: null
*
* @option Boolean widthFixed (optional) Boolean flag indicating if tablesorter
* should apply fixed widths to the table columns. This is usefull when
* using the pager companion plugin. This options requires the dimension
* jquery plugin. Default value: false
*
* @option Boolean cancelSelection (optional) Boolean flag indicating if
* tablesorter should cancel selection of the table headers text.
* Default value: true
*
* @option Boolean debug (optional) Boolean flag indicating if tablesorter
* should display debuging information usefull for development.
*
* @type jQuery
*
* @name tablesorter
*
* @cat Plugins/Tablesorter
*
* @author Christian Bach/christian.bach@polyester.se
*/
(function ($) {
$.extend({
tablesorter: new
function () {
var parsers = [],
widgets = [];
this.defaults = {
cssHeader: "header",
cssAsc: "headerSortUp",
cssDesc: "headerSortDown",
cssChildRow: "expand-child",
sortInitialOrder: "asc",
sortMultiSortKey: "shiftKey",
sortForce: null,
sortAppend: null,
sortLocaleCompare: true,
textExtraction: "simple",
parsers: {}, widgets: [],
widgetZebra: {
css: ["even", "odd"]
}, headers: {}, widthFixed: false,
cancelSelection: true,
sortList: [],
headerList: [],
dateFormat: "us",
decimal: '/\.|\,/g',
onRenderHeader: null,
selectorHeaders: 'thead th',
debug: false
};
/* debuging utils */
function benchmark(s, d) {
log(s + "," + (new Date().getTime() - d.getTime()) + "ms");
}
this.benchmark = benchmark;
function log(s) {
if (typeof console != "undefined" && typeof console.debug != "undefined") {
console.log(s);
} else {
alert(s);
}
}
/* parsers utils */
function buildParserCache(table, $headers) {
if (table.config.debug) {
var parsersDebug = "";
}
if (table.tBodies.length == 0) return; // In the case of empty tables
var rows = table.tBodies[0].rows;
if (rows[0]) {
var list = [],
cells = rows[0].cells,
l = cells.length;
for (var i = 0; i < l; i++) {
var p = false;
if ($.metadata && ($($headers[i]).metadata() && $($headers[i]).metadata().sorter)) {
p = getParserById($($headers[i]).metadata().sorter);
} else if ((table.config.headers[i] && table.config.headers[i].sorter)) {
p = getParserById(table.config.headers[i].sorter);
}
if (!p) {
p = detectParserForColumn(table, rows, -1, i);
}
if (table.config.debug) {
parsersDebug += "column:" + i + " parser:" + p.id + "\n";
}
list.push(p);
}
}
if (table.config.debug) {
log(parsersDebug);
}
return list;
};
function detectParserForColumn(table, rows, rowIndex, cellIndex) {
var l = parsers.length,
node = false,
nodeValue = false,
keepLooking = true;
while (nodeValue == '' && keepLooking) {
rowIndex++;
if (rows[rowIndex]) {
node = getNodeFromRowAndCellIndex(rows, rowIndex, cellIndex);
nodeValue = trimAndGetNodeText(table.config, node);
if (table.config.debug) {
log('Checking if value was empty on row:' + rowIndex);
}
} else {
keepLooking = false;
}
}
for (var i = 1; i < l; i++) {
if (parsers[i].is(nodeValue, table, node)) {
return parsers[i];
}
}
// 0 is always the generic parser (text)
return parsers[0];
}
function getNodeFromRowAndCellIndex(rows, rowIndex, cellIndex) {
return rows[rowIndex].cells[cellIndex];
}
function trimAndGetNodeText(config, node) {
return $.trim(getElementText(config, node));
}
function getParserById(name) {
var l = parsers.length;
for (var i = 0; i < l; i++) {
if (parsers[i].id.toLowerCase() == name.toLowerCase()) {
return parsers[i];
}
}
return false;
}
/* utils */
function buildCache(table) {
if (table.config.debug) {
var cacheTime = new Date();
}
var totalRows = (table.tBodies[0] && table.tBodies[0].rows.length) || 0,
totalCells = (table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length) || 0,
parsers = table.config.parsers,
cache = {
row: [],
normalized: []
};
for (var i = 0; i < totalRows; ++i) {
/** Add the table data to main data array */
var c = $(table.tBodies[0].rows[i]),
cols = [];
// if this is a child row, add it to the last row's children and
// continue to the next row
if (c.hasClass(table.config.cssChildRow)) {
cache.row[cache.row.length - 1] = cache.row[cache.row.length - 1].add(c);
// go to the next for loop
continue;
}
cache.row.push(c);
for (var j = 0; j < totalCells; ++j) {
cols.push(parsers[j].format(getElementText(table.config, c[0].cells[j]), table, c[0].cells[j]));
}
cols.push(cache.normalized.length); // add position for rowCache
cache.normalized.push(cols);
cols = null;
};
if (table.config.debug) {
benchmark("Building cache for " + totalRows + " rows:", cacheTime);
}
return cache;
};
function getElementText(config, node) {
if (!node) return "";
var $node = $(node),
data = $node.attr('data-sort-value');
if (data !== undefined) return data;
var text = "";
if (!config.supportsTextContent) config.supportsTextContent = node.textContent || false;
if (config.textExtraction == "simple") {
if (config.supportsTextContent) {
text = node.textContent;
} else {
if (node.childNodes[0] && node.childNodes[0].hasChildNodes()) {
text = node.childNodes[0].innerHTML;
} else {
text = node.innerHTML;
}
}
} else {
if (typeof(config.textExtraction) == "function") {
text = config.textExtraction(node);
} else {
text = $(node).text();
}
}
return text;
}
function appendToTable(table, cache) {
if (table.config.debug) {
var appendTime = new Date()
}
var c = cache,
r = c.row,
n = c.normalized,
totalRows = n.length,
checkCell = (n[0].length - 1),
tableBody = $(table.tBodies[0]),
rows = [];
for (var i = 0; i < totalRows; i++) {
var pos = n[i][checkCell];
rows.push(r[pos]);
if (!table.config.appender) {
//var o = ;
var l = r[pos].length;
for (var j = 0; j < l; j++) {
tableBody[0].appendChild(r[pos][j]);
}
//
}
}
if (table.config.appender) {
table.config.appender(table, rows);
}
rows = null;
if (table.config.debug) {
benchmark("Rebuilt table:", appendTime);
}
// apply table widgets
applyWidget(table);
// trigger sortend
setTimeout(function () {
$(table).trigger("sortEnd");
}, 0);
};
function buildHeaders(table) {
if (table.config.debug) {
var time = new Date();
}
var meta = ($.metadata) ? true : false;
var header_index = computeTableHeaderCellIndexes(table);
var $tableHeaders = $(table.config.selectorHeaders, table).each(function (index) {
this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex];
// this.column = index;
this.order = formatSortingOrder(table.config.sortInitialOrder);
this.count = this.order;
if (checkHeaderMetadata(this) || checkHeaderOptions(table, index)) this.sortDisabled = true;
if (checkHeaderOptionsSortingLocked(table, index)) this.order = this.lockedOrder = checkHeaderOptionsSortingLocked(table, index);
if (!this.sortDisabled) {
var $th = $(this).addClass(table.config.cssHeader);
if (table.config.onRenderHeader) table.config.onRenderHeader.apply($th);
}
// add cell to headerList
table.config.headerList[index] = this;
});
if (table.config.debug) {
benchmark("Built headers:", time);
log($tableHeaders);
}
return $tableHeaders;
};
// from:
// http://www.javascripttoolbox.com/lib/table/examples.php
// http://www.javascripttoolbox.com/temp/table_cellindex.html
function computeTableHeaderCellIndexes(t) {
var matrix = [];
var lookup = {};
var thead = t.getElementsByTagName('THEAD')[0];
var trs = thead.getElementsByTagName('TR');
for (var i = 0; i < trs.length; i++) {
var cells = trs[i].cells;
for (var j = 0; j < cells.length; j++) {
var c = cells[j];
var rowIndex = c.parentNode.rowIndex;
var cellId = rowIndex + "-" + c.cellIndex;
var rowSpan = c.rowSpan || 1;
var colSpan = c.colSpan || 1
var firstAvailCol;
if (typeof(matrix[rowIndex]) == "undefined") {
matrix[rowIndex] = [];
}
// Find first available column in the first row
for (var k = 0; k < matrix[rowIndex].length + 1; k++) {
if (typeof(matrix[rowIndex][k]) == "undefined") {
firstAvailCol = k;
break;
}
}
lookup[cellId] = firstAvailCol;
for (var k = rowIndex; k < rowIndex + rowSpan; k++) {
if (typeof(matrix[k]) == "undefined") {
matrix[k] = [];
}
var matrixrow = matrix[k];
for (var l = firstAvailCol; l < firstAvailCol + colSpan; l++) {
matrixrow[l] = "x";
}
}
}
}
return lookup;
}
function checkCellColSpan(table, rows, row) {
var arr = [],
r = table.tHead.rows,
c = r[row].cells;
for (var i = 0; i < c.length; i++) {
var cell = c[i];
if (cell.colSpan > 1) {
arr = arr.concat(checkCellColSpan(table, headerArr, row++));
} else {
if (table.tHead.length == 1 || (cell.rowSpan > 1 || !r[row + 1])) {
arr.push(cell);
}
// headerArr[row] = (i+row);
}
}
return arr;
};
function checkHeaderMetadata(cell) {
if (($.metadata) && ($(cell).metadata().sorter === false)) {
return true;
};
return false;
}
function checkHeaderOptions(table, i) {
if ((table.config.headers[i]) && (table.config.headers[i].sorter === false)) {
return true;
};
return false;
}
function checkHeaderOptionsSortingLocked(table, i) {
if ((table.config.headers[i]) && (table.config.headers[i].lockedOrder)) return table.config.headers[i].lockedOrder;
return false;
}
function applyWidget(table) {
var c = table.config.widgets;
var l = c.length;
for (var i = 0; i < l; i++) {
getWidgetById(c[i]).format(table);
}
}
function getWidgetById(name) {
var l = widgets.length;
for (var i = 0; i < l; i++) {
if (widgets[i].id.toLowerCase() == name.toLowerCase()) {
return widgets[i];
}
}
};
function formatSortingOrder(v) {
if (typeof(v) != "Number") {
return (v.toLowerCase() == "desc") ? 1 : 0;
} else {
return (v == 1) ? 1 : 0;
}
}
function isValueInArray(v, a) {
var l = a.length;
for (var i = 0; i < l; i++) {
if (a[i][0] == v) {
return true;
}
}
return false;
}
function setHeadersCss(table, $headers, list, css) {
// remove all header information
$headers.removeClass(css[0]).removeClass(css[1]);
var h = [];
$headers.each(function (offset) {
if (!this.sortDisabled) {
h[this.column] = $(this);
}
});
var l = list.length;
for (var i = 0; i < l; i++) {
h[list[i][0]].addClass(css[list[i][1]]);
}
}
function fixColumnWidth(table, $headers) {
var c = table.config;
if (c.widthFixed) {
var colgroup = $('<colgroup>');
$("tr:first td", table.tBodies[0]).each(function () {
colgroup.append($('<col>').css('width', $(this).width()));
});
$(table).prepend(colgroup);
};
}
function updateHeaderSortCount(table, sortList) {
var c = table.config,
l = sortList.length;
for (var i = 0; i < l; i++) {
var s = sortList[i],
o = c.headerList[s[0]];
o.count = s[1];
o.count++;
}
}
/* sorting methods */
var sortWrapper;
function multisort(table, sortList, cache) {
if (table.config.debug) {
var sortTime = new Date();
}
var dynamicExp = "sortWrapper = function(a,b) {",
l = sortList.length;
// TODO: inline functions.
for (var i = 0; i < l; i++) {
var c = sortList[i][0];
var order = sortList[i][1];
// var s = (getCachedSortType(table.config.parsers,c) == "text") ?
// ((order == 0) ? "sortText" : "sortTextDesc") : ((order == 0) ?
// "sortNumeric" : "sortNumericDesc");
// var s = (table.config.parsers[c].type == "text") ? ((order == 0)
// ? makeSortText(c) : makeSortTextDesc(c)) : ((order == 0) ?
// makeSortNumeric(c) : makeSortNumericDesc(c));
var s = (table.config.parsers[c].type == "text") ? ((order == 0) ? makeSortFunction("text", "asc", c) : makeSortFunction("text", "desc", c)) : ((order == 0) ? makeSortFunction("numeric", "asc", c) : makeSortFunction("numeric", "desc", c));
var e = "e" + i;
dynamicExp += "var " + e + " = " + s; // + "(a[" + c + "],b[" + c
// + "]); ";
dynamicExp += "if(" + e + ") { return " + e + "; } ";
dynamicExp += "else { ";
}
// if value is the same keep orignal order
var orgOrderCol = cache.normalized[0].length - 1;
dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];";
for (var i = 0; i < l; i++) {
dynamicExp += "}; ";
}
dynamicExp += "return 0; ";
dynamicExp += "}; ";
if (table.config.debug) {
benchmark("Evaling expression:" + dynamicExp, new Date());
}
eval(dynamicExp);
cache.normalized.sort(sortWrapper);
if (table.config.debug) {
benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time:", sortTime);
}
return cache;
};
function makeSortFunction(type, direction, index) {
var a = "a[" + index + "]",
b = "b[" + index + "]";
if (type == 'text' && direction == 'asc') {
return "(" + a + " == " + b + " ? 0 : (" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : (" + a + " < " + b + ") ? -1 : 1 )));";
} else if (type == 'text' && direction == 'desc') {
return "(" + a + " == " + b + " ? 0 : (" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : (" + b + " < " + a + ") ? -1 : 1 )));";
} else if (type == 'numeric' && direction == 'asc') {
return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + a + " - " + b + "));";
} else if (type == 'numeric' && direction == 'desc') {
return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + b + " - " + a + "));";
}
};
function makeSortText(i) {
return "((a[" + i + "] < b[" + i + "]) ? -1 : ((a[" + i + "] > b[" + i + "]) ? 1 : 0));";
};
function makeSortTextDesc(i) {
return "((b[" + i + "] < a[" + i + "]) ? -1 : ((b[" + i + "] > a[" + i + "]) ? 1 : 0));";
};
function makeSortNumeric(i) {
return "a[" + i + "]-b[" + i + "];";
};
function makeSortNumericDesc(i) {
return "b[" + i + "]-a[" + i + "];";
};
function sortText(a, b) {
if (table.config.sortLocaleCompare) return a.localeCompare(b);
return ((a < b) ? -1 : ((a > b) ? 1 : 0));
};
function sortTextDesc(a, b) {
if (table.config.sortLocaleCompare) return b.localeCompare(a);
return ((b < a) ? -1 : ((b > a) ? 1 : 0));
};
function sortNumeric(a, b) {
return a - b;
};
function sortNumericDesc(a, b) {
return b - a;
};
function getCachedSortType(parsers, i) {
return parsers[i].type;
}; /* public methods */
this.construct = function (settings) {
return this.each(function () {
// if no thead or tbody quit.
if (!this.tHead || !this.tBodies) return;
// declare
var $this, $document, $headers, cache, config, shiftDown = 0,
sortOrder;
// new blank config object
this.config = {};
// merge and extend.
config = $.extend(this.config, $.tablesorter.defaults, settings);
// store common expression for speed
$this = $(this);
// save the settings where they read
$.data(this, "tablesorter", config);
// build headers
$headers = buildHeaders(this);
// try to auto detect column type, and store in tables config
this.config.parsers = buildParserCache(this, $headers);
// build the cache for the tbody cells
cache = buildCache(this);
// get the css class names, could be done else where.
var sortCSS = [config.cssDesc, config.cssAsc];
// fixate columns if the users supplies the fixedWidth option
fixColumnWidth(this);
// apply event handling to headers
// this is to big, perhaps break it out?
$headers.click(
function (e) {
var totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0;
if (!this.sortDisabled && totalRows > 0) {
// Only call sortStart if sorting is
// enabled.
$this.trigger("sortStart");
// store exp, for speed
var $cell = $(this);
// get current column index
var i = this.column;
// get current column sort order
this.order = this.count++ % 2;
// always sort on the locked order.
if(this.lockedOrder) this.order = this.lockedOrder;
// user only whants to sort on one
// column
if (!e[config.sortMultiSortKey]) {
// flush the sort list
config.sortList = [];
if (config.sortForce != null) {
var a = config.sortForce;
for (var j = 0; j < a.length; j++) {
if (a[j][0] != i) {
config.sortList.push(a[j]);
}
}
}
// add column to sort list
config.sortList.push([i, this.order]);
// multi column sorting
} else {
// the user has clicked on an all
// ready sortet column.
if (isValueInArray(i, config.sortList)) {
// revers the sorting direction
// for all tables.
for (var j = 0; j < config.sortList.length; j++) {
var s = config.sortList[j],
o = config.headerList[s[0]];
if (s[0] == i) {
o.count = s[1];
o.count++;
s[1] = o.count % 2;
}
}
} else {
// add column to sort list array
config.sortList.push([i, this.order]);
}
};
setTimeout(function () {
// set css for headers
setHeadersCss($this[0], $headers, config.sortList, sortCSS);
appendToTable(
$this[0], multisort(
$this[0], config.sortList, cache)
);
}, 1);
// stop normal event by returning false
return false;
}
// cancel selection
}).mousedown(function () {
if (config.cancelSelection) {
this.onselectstart = function () {
return false
};
return false;
}
});
// apply easy methods that trigger binded events
$this.bind("update", function () {
var me = this;
setTimeout(function () {
// rebuild parsers.
me.config.parsers = buildParserCache(
me, $headers);
// rebuild the cache map
cache = buildCache(me);
}, 1);
}).bind("updateCell", function (e, cell) {
var config = this.config;
// get position from the dom.
var pos = [(cell.parentNode.rowIndex - 1), cell.cellIndex];
// update cache
cache.normalized[pos[0]][pos[1]] = config.parsers[pos[1]].format(
getElementText(config, cell), cell);
}).bind("sorton", function (e, list) {
$(this).trigger("sortStart");
config.sortList = list;
// update and store the sortlist
var sortList = config.sortList;
// update header count index
updateHeaderSortCount(this, sortList);
// set css for headers
setHeadersCss(this, $headers, sortList, sortCSS);
// sort the table and append it to the dom
appendToTable(this, multisort(this, sortList, cache));
}).bind("appendCache", function () {
appendToTable(this, cache);
}).bind("applyWidgetId", function (e, id) {
getWidgetById(id).format(this);
}).bind("applyWidgets", function () {
// apply widgets
applyWidget(this);
});
if ($.metadata && ($(this).metadata() && $(this).metadata().sortlist)) {
config.sortList = $(this).metadata().sortlist;
}
// if user has supplied a sort list to constructor.
if (config.sortList.length > 0) {
$this.trigger("sorton", [config.sortList]);
}
// apply widgets
applyWidget(this);
});
};
this.addParser = function (parser) {
var l = parsers.length,
a = true;
for (var i = 0; i < l; i++) {
if (parsers[i].id.toLowerCase() == parser.id.toLowerCase()) {
a = false;
}
}
if (a) {
parsers.push(parser);
};
};
this.addWidget = function (widget) {
widgets.push(widget);
};
this.formatFloat = function (s) {
var i = parseFloat(s);
return (isNaN(i)) ? 0 : i;
};
this.formatInt = function (s) {
var i = parseInt(s);
return (isNaN(i)) ? 0 : i;
};
this.isDigit = function (s, config) {
// replace all an wanted chars and match.
return /^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g, '')));
};
this.clearTableBody = function (table) {
if ($.browser.msie) {
while (table.tBodies[0].firstChild) {
table.tBodies[0].removeChild(table.tBodies[0].firstChild);
}
} else {
table.tBodies[0].innerHTML = "";
}
};
}
});
// extend plugin scope
$.fn.extend({
tablesorter: $.tablesorter.construct
});
// make shortcut
var ts = $.tablesorter;
// add default parsers
ts.addParser({
id: "text",
is: function (s) {
return true;
}, format: function (s) {
return $.trim(s.toLocaleLowerCase());
}, type: "text"
});
ts.addParser({
id: "digit",
is: function (s, table) {
var c = table.config;
return $.tablesorter.isDigit(s, c);
}, format: function (s) {
return $.tablesorter.formatFloat(s);
}, type: "numeric"
});
ts.addParser({
id: "currency",
is: function (s) {
return /^[£$€?.]/.test(s);
}, format: function (s) {
return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g), ""));
}, type: "numeric"
});
ts.addParser({
id: "ipAddress",
is: function (s) {
return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);
}, format: function (s) {
var a = s.split("."),
r = "",
l = a.length;
for (var i = 0; i < l; i++) {
var item = a[i];
if (item.length == 2) {
r += "0" + item;
} else {
r += item;
}
}
return $.tablesorter.formatFloat(r);
}, type: "numeric"
});
ts.addParser({
id: "url",
is: function (s) {
return /^(https?|ftp|file):\/\/$/.test(s);
}, format: function (s) {
return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//), ''));
}, type: "text"
});
ts.addParser({
id: "isoDate",
is: function (s) {
return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);
}, format: function (s) {
return $.tablesorter.formatFloat((s != "") ? new Date(s.replace(
new RegExp(/-/g), "/")).getTime() : "0");
}, type: "numeric"
});
ts.addParser({
id: "percent",
is: function (s) {
return /\%$/.test($.trim(s));
}, format: function (s) {
return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g), ""));
}, type: "numeric"
});
ts.addParser({
id: "usLongDate",
is: function (s) {
return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));
}, format: function (s) {
return $.tablesorter.formatFloat(new Date(s).getTime());
}, type: "numeric"
});
ts.addParser({
id: "shortDate",
is: function (s) {
return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);
}, format: function (s, table) {
var c = table.config;
s = s.replace(/\-/g, "/");
if (c.dateFormat == "us") {
// reformat the string in ISO format
s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$1/$2");
}
if (c.dateFormat == "pt") {
s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");
} else if (c.dateFormat == "uk") {
// reformat the string in ISO format
s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");
} else if (c.dateFormat == "dd/mm/yy" || c.dateFormat == "dd-mm-yy") {
s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$1/$2/$3");
}
return $.tablesorter.formatFloat(new Date(s).getTime());
}, type: "numeric"
});
ts.addParser({
id: "time",
is: function (s) {
return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);
}, format: function (s) {
return $.tablesorter.formatFloat(new Date("2000/01/01 " + s).getTime());
}, type: "numeric"
});
ts.addParser({
id: "metadata",
is: function (s) {
return false;
}, format: function (s, table, cell) {
var c = table.config,
p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName;
return $(cell).metadata()[p];
}, type: "numeric"
});
// add default widgets
ts.addWidget({
id: "zebra",
format: function (table) {
if (table.config.debug) {
var time = new Date();
}
var $tr, row = -1,
odd;
// loop through the visible rows
$("tr:visible", table.tBodies[0]).each(function (i) {
$tr = $(this);
// style children rows the same way the parent
// row was styled
if (!$tr.hasClass(table.config.cssChildRow)) row++;
odd = (row % 2 == 0);
$tr.removeClass(
table.config.widgetZebra.css[odd ? 0 : 1]).addClass(
table.config.widgetZebra.css[odd ? 1 : 0])
});
if (table.config.debug) {
$.tablesorter.benchmark("Applying Zebra widget", time);
}
}
});
})(jQuery);
================================================
FILE: src/Profiler/view/base/web/js/lib/jquery.treetable.js
================================================
/*
* jQuery treetable Plugin 3.2.0
* http://ludo.cubicphuse.nl/jquery-treetable
*
* Copyright 2013, Ludo van den Boom
* Dual licensed under the MIT or GPL Version 2 licenses.
*/
(function($) {
"use strict";
var Node, Tree, methods;
Node = (function() {
function Node(row, tree, settings) {
var parentId;
this.row = row;
this.tree = tree;
this.settings = settings;
// TODO Ensure id/parentId is always a string (not int)
this.id = this.row.data(this.settings.nodeIdAttr);
// TODO Move this to a setParentId function?
parentId = this.row.data(this.settings.parentIdAttr);
if (parentId != null && parentId !== "") {
this.parentId = parentId;
}
this.treeCell = $(this.row.children(this.settings.columnElType)[this.settings.column]);
this.expander = $(this.settings.expanderTemplate);
this.indenter = $(this.settings.indenterTemplate);
this.children = [];
this.initialized = false;
this.treeCell.prepend(this.indenter);
}
Node.prototype.addChild = function(child) {
return this.children.push(child);
};
Node.prototype.ancestors = function() {
var ancestors, node;
node = this;
ancestors = [];
while (node = node.parentNode()) {
ancestors.push(node);
}
return ancestors;
};
Node.prototype.collapse = function() {
if (this.collapsed()) {
return this;
}
this.row.removeClass("expanded").addClass("collapsed");
this._hideChildren();
this.expander.attr("title", this.settings.stringExpand);
if (this.initialized && this.settings.onNodeCollapse != null) {
this.settings.onNodeCollapse.apply(this);
}
return this;
};
Node.prototype.collapsed = function() {
return this.row.hasClass("collapsed");
};
// TODO destroy: remove event handlers, expander, indenter, etc.
Node.prototype.expand = function() {
if (this.expanded()) {
return this;
}
this.row.removeClass("collapsed").addClass("expanded");
if (this.initialized && this.settings.onNodeExpand != null) {
this.settings.onNodeExpand.apply(this);
}
if ($(this.row).is(":visible")) {
this._showChildren();
}
this.expander.attr("title", this.settings.stringCollapse);
return this;
};
Node.prototype.expanded = function() {
return this.row.hasClass("expanded");
};
Node.prototype.hide = function() {
this._hideChildren();
this.row.hide();
return this;
};
Node.prototype.isBranchNode = function() {
if(this.children.length > 0 || this.row.data(this.settings.branchAttr) === true) {
return true;
} else {
return false;
}
};
Node.prototype.updateBranchLeafClass = function(){
this.row.removeClass('branch');
this.row.removeClass('leaf');
this.row.addClass(this.isBranchNode() ? 'branch' : 'leaf');
};
Node.prototype.level = function() {
return this.ancestors().length;
};
Node.prototype.parentNode = function() {
if (this.parentId != null) {
return this.tree[this.parentId];
} else {
return null;
}
};
Node.prototype.removeChild = function(child) {
var i = $.inArray(child, this.children);
return this.children.splice(i, 1)
};
Node.prototype.render = function() {
var handler,
settings = this.settings,
target;
if (settings.expandable === true && this.isBranchNode()) {
handler = function(e) {
$(this).parents("table").treetable("node", $(this).parents("tr").data(settings.nodeIdAttr)).toggle();
return e.preventDefault();
};
this.indenter.html(this.expander);
target = settings.clickableNodeNames === true ? this.treeCell : this.expander;
target.off("click.treetable").on("click.treetable", handler);
target.off("keydown.treetable").on("keydown.treetable", function(e) {
if (e.keyCode == 13) {
handler.apply(this, [e]);
}
});
}
this.indenter[0].style.paddingLeft = "" + (this.level() * settings.indent) + "px";
return this;
};
Node.prototype.reveal = function() {
if (this.parentId != null) {
this.parentNode().reveal();
}
return this.expand();
};
Node.prototype.setParent = function(node) {
if (this.parentId != null) {
this.tree[this.parentId].removeChild(this);
}
this.parentId = node.id;
this.row.data(this.settings.parentIdAttr, node.id);
return node.addChild(this);
};
Node.prototype.show = function() {
if (!this.initialized) {
this._initialize();
}
this.row.show();
if (this.expanded()) {
this._showChildren();
}
return this;
};
Node.prototype.toggle = function() {
if (this.expanded()) {
this.collapse();
} else {
this.expand();
}
return this;
};
Node.prototype._hideChildren = function() {
var child, _i, _len, _ref, _results;
_ref = this.children;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
_results.push(child.hide());
}
return _results;
};
Node.prototype._initialize = function() {
var settings = this.settings;
this.render();
if (settings.expandable === true && settings.initialState === "collapsed") {
this.collapse();
} else {
this.expand();
}
if (settings.onNodeInitialized != null) {
settings.onNodeInitialized.apply(this);
}
return this.initialized = true;
};
Node.prototype._showChildren = function() {
var child, _i, _len, _ref, _results;
_ref = this.children;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
_results.push(child.show());
}
return _results;
};
return Node;
})();
Tree = (function() {
function Tree(table, settings) {
this.table = table;
this.settings = settings;
this.tree = {};
// Cache the nodes and roots in simple arrays for quick access/iteration
this.nodes = [];
this.roots = [];
}
Tree.prototype.collapseAll = function() {
var node, _i, _len, _ref, _results;
_ref = this.nodes;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i];
_results.push(node.collapse());
}
return _results;
};
Tree.prototype.expandAll = function() {
var node, _i, _len, _ref, _results;
_ref = this.nodes;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i];
_results.push(node.expand());
}
return _results;
};
Tree.prototype.findLastNode = function (node) {
if (node.children.length > 0) {
return this.findLastNode(node.children[node.children.length - 1]);
} else {
return node;
}
};
Tree.prototype.loadRows = function(rows) {
var node, row, i;
if (rows != null) {
for (i = 0; i < rows.length; i++) {
row = $(rows[i]);
if (row.data(this.settings.nodeIdAttr) != null) {
node = new Node(row, this.tree, this.settings);
this.nodes.push(node);
this.tree[node.id] = node;
if (node.parentId != null && this.tree[node.parentId]) {
this.tree[node.parentId].addChild(node);
} else {
this.roots.push(node);
}
}
}
}
for (i = 0; i < this.nodes.length; i++) {
node = this.nodes[i].updateBranchLeafClass();
}
return this;
};
Tree.prototype.move = function(node, destination) {
// Conditions:
// 1: +node+ should not be inserted as a child of +node+ itself.
// 2: +destination+ should not be the same as +node+'s current parent (this
// prevents +node+ from being moved to the same location where it already
// is).
// 3: +node+ should not be inserted in a location in a branch if this would
// result in +node+ being an ancestor of itself.
var nodeParent = node.parentNode();
if (node !== destination && destination.id !== node.parentId && $.inArray(node, destination.ancestors()) === -1) {
node.setParent(destination);
this._moveRows(node, destination);
// Re-render parentNode if this is its first child node, and therefore
// doesn't have the expander yet.
if (node.parentNode().children.length === 1) {
node.parentNode().render();
}
}
if(nodeParent){
nodeParent.updateBranchLeafClass();
}
if(node.parentNode()){
node.parentNode().updateBranchLeafClass();
}
node.updateBranchLeafClass();
return this;
};
Tree.prototype.removeNode = function(node) {
// Recursively remove all descendants of +node+
this.unloadBranch(node);
// Remove node from DOM (<tr>)
node.row.remove();
// Remove node from parent children list
if (node.parentId != null) {
node.parentNode().removeChild(node);
}
// Clean up Tree object (so Node objects are GC-ed)
delete this.tree[node.id];
this.nodes.splice($.inArray(node, this.nodes), 1);
return this;
}
Tree.prototype.render = function() {
var root, _i, _len, _ref;
_ref = this.roots;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
root = _ref[_i];
// Naming is confusing (show/render). I do not call render on node from
// here.
root.show();
}
return this;
};
Tree.prototype.sortBranch = function(node, sortFun) {
// First sort internal array of children
node.children.sort(sortFun);
// Next render rows in correct order on page
this._sortChildRows(node);
return this;
};
Tree.prototype.unloadBranch = function(node) {
// Use a copy of the children array to not have other functions interfere
// with this function if they manipulate the children array
// (eg removeNode).
var children = node.children.slice(0),
i;
for (i = 0; i < children.length; i++) {
this.removeNode(children[i]);
}
// Reset node's collection of children
node.children = [];
node.updateBranchLeafClass();
return this;
};
Tree.prototype._moveRows = function(node, destination) {
var children = node.children, i;
node.row.insertAfter(destination.row);
node.render();
// Loop backwards through children to have them end up on UI in correct
// order (see #112)
for (i = children.length - 1; i >= 0; i--) {
this._moveRows(children[i], node);
}
};
// Special _moveRows case, move children to itself to force sorting
Tree.prototype._sortChildRows = function(parentNode) {
return this._moveRows(parentNode, parentNode);
};
return Tree;
})();
// jQuery Plugin
methods = {
init: function(options, force) {
var settings;
settings = $.extend({
branchAttr: "ttBranch",
clickableNodeNames: false,
column: 0,
columnElType: "td", // i.e. 'td', 'th' or 'td,th'
expandable: false,
expanderTemplate: "<a href='#'> </a>",
indent: 19,
indenterTemplate: "<span class='indenter'></span>",
initialState: "collapsed",
nodeIdAttr: "ttId", // maps to data-tt-id
parentIdAttr: "ttParentId", // maps to data-tt-parent-id
stringExpand: "Expand",
stringCollapse: "Collapse",
// Events
onInitialized: null,
onNodeCollapse: null,
onNodeExpand: null,
onNodeInitialized: null
}, options);
return this.each(function() {
var el = $(this), tree;
if (force || el.data("treetable") === undefined) {
tree = new Tree(this, settings);
tree.loadRows(this.rows).render();
el.addClass("treetable").data("treetable", tree);
if (settings.onInitialized != null) {
settings.onInitialized.apply(tree);
}
}
return el;
});
},
destroy: function() {
return this.each(function() {
return $(this).removeData("treetable").removeClass("treetable");
});
},
collapseAll: function() {
this.data("treetable").collapseAll();
return this;
},
collapseNode: function(id) {
var node = this.data("treetable").tree[id];
if (node) {
node.collapse();
} else {
throw new Error("Unknown node '" + id + "'");
}
return this;
},
expandAll: function() {
this.data("treetable").expandAll();
return this;
},
expandNode: function(id) {
var node = this.data("treetable").tree[id];
if (node) {
if (!node.initialized) {
node._initialize();
}
node.expand();
} else {
throw new Error("Unknown node '" + id + "'");
}
return this;
},
loadBranch: function(node, rows) {
var settings = this.data("treetable").settings,
tree = this.data("treetable").tree;
// TODO Switch to $.parseHTML
rows = $(rows);
if (node == null) { // Inserting new root nodes
this.append(rows);
} else {
var lastNode = this.data("treetable").findLastNode(node);
rows.insertAfter(lastNode.row);
}
this.data("treetable").loadRows(rows);
// Make sure nodes are properly initialized
rows.filter("tr").each(function() {
tree[$(this).data(settings.nodeIdAttr)].show();
});
if (node != null) {
// Re-render parent to ensure expander icon is shown (#79)
node.render().expand();
}
return this;
},
move: function(nodeId, destinationId) {
var destination, node;
node = this.data("treetable").tree[nodeId];
destination = this.data("treetable").tree[destinationId];
this.data("treetable").move(node, destination);
return this;
},
node: function(id) {
return this.data("treetable").tree[id];
},
removeNode: function(id) {
var node = this.data("treetable").tree[id];
if (node) {
this.data("treetable").removeNode(node);
} else {
throw new Error("Unknown node '" + id + "'");
}
return this;
},
reveal: function(id) {
var node = this.data("treetable").tree[id];
if (node) {
node.reveal();
} else {
throw new Error("Unknown node '" + id + "'");
}
return this;
},
sortBranch: function(node, columnOrFunction) {
var settings = this.data("treetable").settings,
prepValue,
sortFun;
columnOrFunction = columnOrFunction || settings.column;
sortFun = columnOrFunction;
if ($.isNumeric(columnOrFunction)) {
sortFun = function(a, b) {
var extractValue, valA, valB;
extractValue = function(node) {
var val = node.row.find("td:eq(" + columnOrFunction + ")").text();
// Ignore trailing/leading whitespace and use uppercase values for
// case insensitive ordering
return $.trim(val).toUpperCase();
}
valA = extractValue(a);
valB = extractValue(b);
if (valA < valB) return -1;
if (valA > valB) return 1;
return 0;
};
}
this.data("treetable").sortBranch(node, sortFun);
return this;
},
unloadBranch: function(node) {
this.data("treetable").unloadBranch(node);
return this;
}
};
$.fn.treetable = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
return $.error("Method " + method + " does not exist on jQuery.treetable");
}
};
// Expose classes to world
window.TreeTable || (window.TreeTable = {});
window.TreeTable.Node = Node;
window.TreeTable.Tree = Tree;
})(jQuery);
================================================
FILE: src/Profiler/view/base/web/js/table.js
================================================
define([
'jquery',
'jquery/jquery.cookie',
'jquery/ui',
'Mirasvit_Profiler/js/lib/jquery.treetable',
'Mirasvit_Profiler/js/lib/jquery.tablesorter',
'Mirasvit_Profiler/js/lib/jquery.filtertable'
], function ($) {
'use strict';
var methods = {
init: function (options) {
var $table = this;
$table.treetable({
expandable: true
});
$table.tablesorter({
callback: function (term, table) {
$table.treetable('expandAll');
}
});
$table.bind("sortStart", function () {
$table.treetable('expandAll');
});
$table.filterTable({
callback: function (term, table) {
$table.treetable('expandAll');
}
});
if ($table.attr('data-threshold')) {
var max = 0;
var min = 1000000;
$('[data-threshold-value]', $table).each(function (i, tr) {
var value = parseInt($(tr).attr('data-threshold-value'));
if (value > max) {
max = value;
}
if (value < max) {
min = value;
}
});
var $threshold = $($table.attr('data-threshold'));
$threshold.slider({
min: min,
max: max,
value: min,
slide: function (event, ui) {
$table.treetable('expandAll');
$('[data-threshold-value]', $table).each(function (i, tr) {
var value = $(tr).attr('data-threshold-value');
if (value > ui.value) {
$(tr).show();
} else {
$(tr).hide();
}
});
$(".value", this).html(ui.value + " ms");
}
});
}
}
};
$.fn.profilerTable = function (method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
}
};
});
gitextract_tcnu8bym/
├── LICENSE.txt
├── README.md
├── composer.json
├── registration.php
└── src/
└── Profiler/
├── Api/
│ └── Data/
│ └── ProfileInterface.php
├── Block/
│ ├── Context.php
│ ├── Profile/
│ │ ├── Listing.php
│ │ └── View.php
│ ├── Tab/
│ │ ├── Code.php
│ │ ├── Database.php
│ │ ├── IO.php
│ │ └── TabInterface.php
│ └── Toolbar.php
├── Console/
│ └── Command/
│ ├── AbstractCommand.php
│ ├── AllowIpsCommand.php
│ ├── DisableCommand.php
│ ├── EnableCommand.php
│ └── StatusCommand.php
├── Controller/
│ ├── Profile/
│ │ ├── Index.php
│ │ └── View.php
│ └── Profile.php
├── Helper/
│ └── Format.php
├── Model/
│ ├── Config.php
│ ├── Driver/
│ │ └── Output/
│ │ └── Html.php
│ └── Storage.php
├── Profile/
│ ├── Code.php
│ ├── Context.php
│ ├── Database.php
│ ├── General.php
│ ├── Meta.php
│ └── Pool.php
├── etc/
│ ├── di.xml
│ ├── frontend/
│ │ └── routes.xml
│ └── module.xml
└── view/
└── base/
├── layout/
│ ├── profiler_profile_index.xml
│ └── profiler_profile_view.xml
├── page_layout/
│ └── profiler.xml
├── templates/
│ ├── profile/
│ │ ├── listing.phtml
│ │ └── view.phtml
│ ├── root.phtml
│ ├── tab/
│ │ ├── code.phtml
│ │ ├── database.phtml
│ │ └── io.phtml
│ └── toolbar.phtml
└── web/
├── css/
│ ├── less/
│ │ ├── _module/
│ │ │ ├── _base.less
│ │ │ ├── _etc.less
│ │ │ └── _tabs.less
│ │ └── _module.less
│ └── module.css
└── js/
├── lib/
│ ├── jquery.filtertable.js
│ ├── jquery.tablesorter.js
│ └── jquery.treetable.js
└── table.js
SYMBOL INDEX (146 symbols across 29 files)
FILE: src/Profiler/Api/Data/ProfileInterface.php
type ProfileInterface (line 4) | interface ProfileInterface
method dump (line 9) | public function dump();
FILE: src/Profiler/Block/Context.php
class Context (line 9) | class Context
method __construct (line 15) | public function __construct(
method getProfile (line 23) | public function getProfile($id = false)
FILE: src/Profiler/Block/Profile/Listing.php
class Listing (line 9) | class Listing extends Template
method __construct (line 16) | public function __construct(
method getList (line 26) | public function getList()
FILE: src/Profiler/Block/Profile/View.php
class View (line 10) | class View extends Template
method __construct (line 27) | public function __construct(
method getProfile (line 39) | public function getProfile()
method getTabs (line 47) | public function getTabs()
FILE: src/Profiler/Block/Tab/Code.php
class Code (line 9) | class Code extends Template implements TabInterface
method __construct (line 21) | public function __construct(
method getLabel (line 34) | public function getLabel()
method getIcon (line 42) | public function getIcon()
method getCodeDump (line 50) | public function getCodeDump()
method getFlameGraphJson (line 55) | public function getFlameGraphJson() {
method _removeChildrenKeys (line 80) | private function _removeChildrenKeys($node) {
method getGeneralDump (line 91) | public function getGeneralDump()
method getLevel (line 96) | public function getLevel($timerId)
method renderTimerId (line 105) | public function renderTimerId($timerId)
method getParentTimerId (line 116) | public function getParentTimerId($timerId)
method getTimerLength (line 128) | public function getTimerLength($timerId)
method getTotalTime (line 139) | public function getTotalTime()
FILE: src/Profiler/Block/Tab/Database.php
class Database (line 10) | class Database extends Template implements TabInterface
method __construct (line 22) | public function __construct(
method getLabel (line 34) | public function getLabel()
method getIcon (line 42) | public function getIcon()
method getDump (line 50) | public function getDump()
method getSlowQueries (line 58) | public function getSlowQueries()
FILE: src/Profiler/Block/Tab/IO.php
class IO (line 9) | class IO extends Template implements TabInterface
method __construct (line 21) | public function __construct(
method getLabel (line 34) | public function getLabel()
method getIcon (line 42) | public function getIcon()
method getDump (line 50) | public function getDump()
FILE: src/Profiler/Block/Tab/TabInterface.php
type TabInterface (line 4) | interface TabInterface
method getIcon (line 9) | public function getIcon();
method getLabel (line 14) | public function getLabel();
FILE: src/Profiler/Block/Toolbar.php
class Toolbar (line 12) | class Toolbar extends Template
method __construct (line 29) | public function __construct(
method getDump (line 43) | public function getDump()
method getProfileUrl (line 51) | public function getProfileUrl()
FILE: src/Profiler/Console/Command/AbstractCommand.php
class AbstractCommand (line 8) | abstract class AbstractCommand extends Command
method __construct (line 22) | public function __construct(
FILE: src/Profiler/Console/Command/AllowIpsCommand.php
class AllowIpsCommand (line 12) | class AllowIpsCommand extends AbstractCommand
method __construct (line 19) | public function __construct(
method configure (line 31) | protected function configure()
method execute (line 59) | protected function execute(InputInterface $input, OutputInterface $out...
FILE: src/Profiler/Console/Command/DisableCommand.php
class DisableCommand (line 11) | class DisableCommand extends AbstractCommand
method __construct (line 18) | public function __construct(
method configure (line 30) | protected function configure()
method execute (line 42) | protected function execute(InputInterface $input, OutputInterface $out...
FILE: src/Profiler/Console/Command/EnableCommand.php
class EnableCommand (line 11) | class EnableCommand extends AbstractCommand
method __construct (line 18) | public function __construct(
method configure (line 30) | protected function configure()
method execute (line 42) | protected function execute(InputInterface $input, OutputInterface $out...
FILE: src/Profiler/Console/Command/StatusCommand.php
class StatusCommand (line 12) | class StatusCommand extends AbstractCommand
method __construct (line 19) | public function __construct(
method configure (line 31) | protected function configure()
method execute (line 43) | protected function execute(InputInterface $input, OutputInterface $out...
FILE: src/Profiler/Controller/Profile.php
class Profile (line 8) | abstract class Profile extends Action
method __construct (line 10) | public function __construct(
FILE: src/Profiler/Controller/Profile/Index.php
class Index (line 7) | class Index extends Profile
method execute (line 9) | public function execute()
FILE: src/Profiler/Controller/Profile/View.php
class View (line 8) | class View extends Profile
method execute (line 10) | public function execute()
FILE: src/Profiler/Helper/Format.php
class Format (line 7) | class Format extends AbstractHelper
method formatTime (line 14) | public function formatTime($number, $isSeconds = false)
method any (line 27) | public function any($any)
FILE: src/Profiler/Model/Config.php
class Config (line 11) | class Config
method __construct (line 40) | public function __construct(
method isEnabled (line 57) | public function isEnabled()
method enableProfiler (line 65) | public function enableProfiler()
method disableProfiler (line 79) | public function disableProfiler()
method enableDbProfiler (line 94) | public function enableDbProfiler()
method disableDbProfiler (line 111) | public function disableDbProfiler()
method setAddresses (line 126) | public function setAddresses($addresses)
method getAddressInfo (line 138) | public function getAddressInfo()
method getDumpPath (line 145) | public function getDumpPath()
FILE: src/Profiler/Model/Driver/Output/Html.php
class Html (line 10) | class Html implements OutputInterface
method display (line 15) | public function display(Stat $stat)
FILE: src/Profiler/Model/Storage.php
class Storage (line 9) | class Storage
method __construct (line 21) | public function __construct(
method dump (line 32) | public function dump()
method load (line 61) | public function load($file)
method getList (line 69) | public function getList()
method cleanup (line 94) | public function cleanup()
FILE: src/Profiler/Profile/Code.php
class Code (line 7) | class Code implements ProfileInterface
method dump (line 12) | public function dump()
method getStat (line 26) | private function getStat()
FILE: src/Profiler/Profile/Context.php
class Context (line 5) | class Context
method getExecutionTime (line 11) | public function getExecutionTime()
method getClientIP (line 19) | public function getClientIP()
method getURI (line 31) | public function getURI()
method isCLI (line 43) | public function isCLI()
method getCliArgs (line 51) | public function getCliArgs()
FILE: src/Profiler/Profile/Database.php
class Database (line 8) | class Database implements ProfileInterface
method __construct (line 25) | public function __construct(
method dump (line 34) | public function dump()
method getProfiler (line 70) | private function getProfiler()
FILE: src/Profiler/Profile/General.php
class General (line 8) | class General implements ProfileInterface
method __construct (line 30) | public function __construct(
method dump (line 41) | public function dump()
FILE: src/Profiler/Profile/Meta.php
class Meta (line 7) | class Meta implements ProfileInterface
method __construct (line 18) | public function __construct(
method dump (line 27) | public function dump()
FILE: src/Profiler/Profile/Pool.php
class Pool (line 7) | class Pool
method __construct (line 14) | public function __construct(
method getProfiles (line 23) | public function getProfiles()
FILE: src/Profiler/view/base/web/js/lib/jquery.tablesorter.js
function benchmark (line 147) | function benchmark(s, d) {
function log (line 153) | function log(s) {
function buildParserCache (line 163) | function buildParserCache(table, $headers) {
function detectParserForColumn (line 210) | function detectParserForColumn(table, rows, rowIndex, cellIndex) {
function getNodeFromRowAndCellIndex (line 236) | function getNodeFromRowAndCellIndex(rows, rowIndex, cellIndex) {
function trimAndGetNodeText (line 240) | function trimAndGetNodeText(config, node) {
function getParserById (line 244) | function getParserById(name) {
function buildCache (line 256) | function buildCache(table) {
function getElementText (line 302) | function getElementText(config, node) {
function appendToTable (line 334) | function appendToTable(table, cache) {
function buildHeaders (line 389) | function buildHeaders(table) {
function computeTableHeaderCellIndexes (line 434) | function computeTableHeaderCellIndexes(t) {
function checkCellColSpan (line 475) | function checkCellColSpan(table, rows, row) {
function checkHeaderMetadata (line 495) | function checkHeaderMetadata(cell) {
function checkHeaderOptions (line 502) | function checkHeaderOptions(table, i) {
function checkHeaderOptionsSortingLocked (line 509) | function checkHeaderOptionsSortingLocked(table, i) {
function applyWidget (line 514) | function applyWidget(table) {
function getWidgetById (line 524) | function getWidgetById(name) {
function formatSortingOrder (line 533) | function formatSortingOrder(v) {
function isValueInArray (line 541) | function isValueInArray(v, a) {
function setHeadersCss (line 551) | function setHeadersCss(table, $headers, list, css) {
function fixColumnWidth (line 568) | function fixColumnWidth(table, $headers) {
function updateHeaderSortCount (line 579) | function updateHeaderSortCount(table, sortList) {
function multisort (line 594) | function multisort(table, sortList, cache) {
function makeSortFunction (line 650) | function makeSortFunction(type, direction, index) {
function makeSortText (line 664) | function makeSortText(i) {
function makeSortTextDesc (line 668) | function makeSortTextDesc(i) {
function makeSortNumeric (line 672) | function makeSortNumeric(i) {
function makeSortNumericDesc (line 676) | function makeSortNumericDesc(i) {
function sortText (line 680) | function sortText(a, b) {
function sortTextDesc (line 685) | function sortTextDesc(a, b) {
function sortNumeric (line 690) | function sortNumeric(a, b) {
function sortNumericDesc (line 694) | function sortNumericDesc(a, b) {
function getCachedSortType (line 698) | function getCachedSortType(parsers, i) {
FILE: src/Profiler/view/base/web/js/lib/jquery.treetable.js
function Node (line 14) | function Node(row, tree, settings) {
function Tree (line 243) | function Tree(table, settings) {
Condensed preview — 53 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (158K chars).
[
{
"path": "LICENSE.txt",
"chars": 10365,
"preview": "\nOpen Software License (\"OSL\") v. 3.0\n\nThis Open Software License (the \"License\") applies to any original work of author"
},
{
"path": "README.md",
"chars": 1509,
"preview": "# Magento 2 Profiler Module #\n\n## Installation\n\nLog in to the Magento server, go to your Magento install dir and run the"
},
{
"path": "composer.json",
"chars": 442,
"preview": "{\n \"name\": \"mirasvit/module-profiler\",\n \"description\": \"Magento 2 Profiler\",\n \"require\": {\n \"magento/framework\": \""
},
{
"path": "registration.php",
"chars": 747,
"preview": "<?php\n$_SERVER['MAGE_PROFILER_STAT'] = new \\Magento\\Framework\\Profiler\\Driver\\Standard\\Stat();\n\n$canEnable = defined('BP"
},
{
"path": "src/Profiler/Api/Data/ProfileInterface.php",
"chars": 140,
"preview": "<?php\nnamespace Mirasvit\\Profiler\\Api\\Data;\n\ninterface ProfileInterface\n{\n /**\n * @return array\n */\n publi"
},
{
"path": "src/Profiler/Block/Context.php",
"chars": 1334,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Block;\n\nuse Magento\\Framework\\App\\RequestInterface;\nuse Magento\\Framework\\App\\Resourc"
},
{
"path": "src/Profiler/Block/Profile/Listing.php",
"chars": 599,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Block\\Profile;\n\nuse Magento\\Framework\\View\\Element\\Template\\Context as TemplateContex"
},
{
"path": "src/Profiler/Block/Profile/View.php",
"chars": 959,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Block\\Profile;\n\nuse Magento\\Framework\\App\\RequestInterface;\nuse Magento\\Framework\\Vie"
},
{
"path": "src/Profiler/Block/Tab/Code.php",
"chars": 3257,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Block\\Tab;\n\nuse Magento\\Framework\\View\\Element\\Template\\Context as TemplateContext;\nu"
},
{
"path": "src/Profiler/Block/Tab/Database.php",
"chars": 1428,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Block\\Tab;\n\nuse Magento\\Framework\\View\\Element\\Template\\Context as TemplateContext;\nu"
},
{
"path": "src/Profiler/Block/Tab/IO.php",
"chars": 972,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Block\\Tab;\n\nuse Magento\\Framework\\View\\Element\\Template\\Context as TemplateContext;\nu"
},
{
"path": "src/Profiler/Block/Tab/TabInterface.php",
"chars": 212,
"preview": "<?php\nnamespace Mirasvit\\Profiler\\Block\\Tab;\n\ninterface TabInterface\n{\n /**\n * @return string\n */\n public "
},
{
"path": "src/Profiler/Block/Toolbar.php",
"chars": 1051,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Block;\n\nuse Magento\\Framework\\View\\Element\\Template\\Context as TemplateContext;\nuse M"
},
{
"path": "src/Profiler/Console/Command/AbstractCommand.php",
"chars": 549,
"preview": "<?php\nnamespace Mirasvit\\Profiler\\Console\\Command;\n\nuse Magento\\Framework\\App\\DeploymentConfig;\nuse Magento\\Framework\\Ap"
},
{
"path": "src/Profiler/Console/Command/AllowIpsCommand.php",
"chars": 2168,
"preview": "<?php\nnamespace Mirasvit\\Profiler\\Console\\Command;\n\nuse Magento\\Framework\\App\\State;\nuse Magento\\Framework\\Exception\\Loc"
},
{
"path": "src/Profiler/Console/Command/DisableCommand.php",
"chars": 1207,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Console\\Command;\n\nuse Magento\\Framework\\App\\State;\nuse Magento\\Framework\\Exception\\Lo"
},
{
"path": "src/Profiler/Console/Command/EnableCommand.php",
"chars": 1203,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Console\\Command;\n\nuse Magento\\Framework\\App\\State;\nuse Magento\\Framework\\Exception\\Lo"
},
{
"path": "src/Profiler/Console/Command/StatusCommand.php",
"chars": 1350,
"preview": "<?php\nnamespace Mirasvit\\Profiler\\Console\\Command;\n\nuse Magento\\Framework\\App\\State;\nuse Magento\\Framework\\Exception\\Loc"
},
{
"path": "src/Profiler/Controller/Profile/Index.php",
"chars": 534,
"preview": "<?php\nnamespace Mirasvit\\Profiler\\Controller\\Profile;\n\nuse Mirasvit\\Profiler\\Controller\\Profile;\nuse Magento\\Framework\\C"
},
{
"path": "src/Profiler/Controller/Profile/View.php",
"chars": 534,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Controller\\Profile;\n\nuse Mirasvit\\Profiler\\Controller\\Profile;\nuse Magento\\Framework\\"
},
{
"path": "src/Profiler/Controller/Profile.php",
"chars": 286,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Controller;\n\nuse Magento\\Framework\\App\\Action\\Action;\nuse Magento\\Framework\\App\\Actio"
},
{
"path": "src/Profiler/Helper/Format.php",
"chars": 757,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Helper;\n\nuse Magento\\Framework\\App\\Helper\\AbstractHelper;\n\nclass Format extends Abstr"
},
{
"path": "src/Profiler/Model/Config.php",
"chars": 3772,
"preview": "<?php\nnamespace Mirasvit\\Profiler\\Model;\n\nuse Magento\\Framework\\App\\Config\\ScopeConfigInterface;\nuse Magento\\Framework\\A"
},
{
"path": "src/Profiler/Model/Driver/Output/Html.php",
"chars": 1696,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Model\\Driver\\Output;\n\nuse Magento\\Framework\\Profiler\\Driver\\Standard\\Stat;\nuse Magent"
},
{
"path": "src/Profiler/Model/Storage.php",
"chars": 2711,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Model;\n\nuse Mirasvit\\Profiler\\Profile\\Pool;\nuse Symfony\\Component\\Yaml\\Dumper as Yaml"
},
{
"path": "src/Profiler/Profile/Code.php",
"chars": 573,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Profile;\n\nuse Mirasvit\\Profiler\\Api\\Data\\ProfileInterface;\n\nclass Code implements Pro"
},
{
"path": "src/Profiler/Profile/Context.php",
"chars": 978,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Profile;\n\nclass Context\n{\n\n /**\n * @return float\n */\n public function g"
},
{
"path": "src/Profiler/Profile/Database.php",
"chars": 2237,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Profile;\n\nuse Magento\\Framework\\App\\ResourceConnection;\nuse Mirasvit\\Profiler\\Api\\Dat"
},
{
"path": "src/Profiler/Profile/General.php",
"chars": 1670,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Profile;\n\nuse Mirasvit\\Profiler\\Api\\Data\\ProfileInterface;\nuse Magento\\Framework\\App\\"
},
{
"path": "src/Profiler/Profile/Meta.php",
"chars": 1109,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Profile;\n\nuse Mirasvit\\Profiler\\Api\\Data\\ProfileInterface;\n\nclass Meta implements Pro"
},
{
"path": "src/Profiler/Profile/Pool.php",
"chars": 416,
"preview": "<?php\n\nnamespace Mirasvit\\Profiler\\Profile;\n\nuse Mirasvit\\Profiler\\Api\\Data\\ProfileInterface;\n\nclass Pool\n{\n /**\n "
},
{
"path": "src/Profiler/etc/di.xml",
"chars": 1885,
"preview": "<config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:noNamespaceSchemaLocation=\"urn:magento:framewo"
},
{
"path": "src/Profiler/etc/frontend/routes.xml",
"chars": 349,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:noNames"
},
{
"path": "src/Profiler/etc/module.xml",
"chars": 273,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<config xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSche"
},
{
"path": "src/Profiler/view/base/layout/profiler_profile_index.xml",
"chars": 487,
"preview": "<?xml version=\"1.0\"?>\n<page xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" layout=\"profiler\"\n xsi:noNamespac"
},
{
"path": "src/Profiler/view/base/layout/profiler_profile_view.xml",
"chars": 481,
"preview": "<?xml version=\"1.0\"?>\n<page xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" layout=\"profiler\"\n xsi:noNamespac"
},
{
"path": "src/Profiler/view/base/page_layout/profiler.xml",
"chars": 303,
"preview": "<?xml version=\"1.0\"?>\n<layout xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"urn:m"
},
{
"path": "src/Profiler/view/base/templates/profile/listing.phtml",
"chars": 1443,
"preview": "<?php\n/** @var \\Mirasvit\\Profiler\\Block\\Profile\\Listing $block */\n\nuse Mirasvit\\Profiler\\Profile\\Meta;\n\n/** @var \\Mirasv"
},
{
"path": "src/Profiler/view/base/templates/profile/view.phtml",
"chars": 1410,
"preview": "<?php\nuse Mirasvit\\Profiler\\Profile\\Meta;\nuse Mirasvit\\Profiler\\Profile\\General;\n\n/** @var \\Mirasvit\\Profiler\\Block\\Prof"
},
{
"path": "src/Profiler/view/base/templates/root.phtml",
"chars": 530,
"preview": "<!doctype html>\n<html>\n<head>\n <script src=\"//code.jquery.com/jquery-3.2.1.min.js\"></script>\n <script src=\"//cdnjs"
},
{
"path": "src/Profiler/view/base/templates/tab/code.phtml",
"chars": 2135,
"preview": "<?php\n/** @var \\Mirasvit\\Profiler\\Block\\Tab\\Code $block */\n\nuse Mirasvit\\Profiler\\Profile\\General;\n\n$general = $block->g"
},
{
"path": "src/Profiler/view/base/templates/tab/database.phtml",
"chars": 1546,
"preview": "<?php\n/** @var \\Mirasvit\\Profiler\\Block\\Tab\\Database $block */\nuse Mirasvit\\Profiler\\Profile\\Database;\n\n$dump = $block->"
},
{
"path": "src/Profiler/view/base/templates/tab/io.phtml",
"chars": 731,
"preview": "<?php\n/** @var \\Mirasvit\\Profiler\\Block\\Tab\\IO $block */\n\nuse Magento\\Framework\\Profiler\\Driver\\Standard\\Stat;\nuse Miras"
},
{
"path": "src/Profiler/view/base/templates/toolbar.phtml",
"chars": 1684,
"preview": "<?php\n/** @var \\Mirasvit\\Profiler\\Block\\Toolbar $block */\nuse Mirasvit\\Profiler\\Profile\\Meta;\nuse Mirasvit\\Profiler\\Prof"
},
{
"path": "src/Profiler/view/base/web/css/less/_module/_base.less",
"chars": 1614,
"preview": "html, body {\n margin: 0;\n font-family: 'Open Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;\n font-style: norm"
},
{
"path": "src/Profiler/view/base/web/css/less/_module/_etc.less",
"chars": 672,
"preview": ".metric {\n border: 1px solid @border-light;\n display: inline-block;\n margin: 10px 10px 10px 0;\n\n strong {\n text-a"
},
{
"path": "src/Profiler/view/base/web/css/less/_module/_tabs.less",
"chars": 726,
"preview": ".tabs-container {\n .tabs {\n list-style: none;\n padding: 0 10px 0 10px;\n margin: 0;\n background: @bg-dark;\n\n"
},
{
"path": "src/Profiler/view/base/web/css/less/_module.less",
"chars": 363,
"preview": "@import url('//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css');\n\n@text-color: #303030;\n@link-color"
},
{
"path": "src/Profiler/view/base/web/css/module.css",
"chars": 2878,
"preview": "@import url('//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css');\nhtml,\nbody {\n margin: 0;\n font-f"
},
{
"path": "src/Profiler/view/base/web/js/lib/jquery.filtertable.js",
"chars": 15767,
"preview": "/**\n * jquery.filterTable\n *\n * This plugin will add a search filter to tables. When typing in the filter,\n * any rows t"
},
{
"path": "src/Profiler/view/base/web/js/lib/jquery.tablesorter.js",
"chars": 43718,
"preview": "/*\n *\n * TableSorter 2.0 - Client-side table sorting with ease!\n * Version 2.0.5b\n * @requires jQuery v1.2.3\n *\n * Copyr"
},
{
"path": "src/Profiler/view/base/web/js/lib/jquery.treetable.js",
"chars": 19725,
"preview": "/*\n * jQuery treetable Plugin 3.2.0\n * http://ludo.cubicphuse.nl/jquery-treetable\n *\n * Copyright 2013, Ludo van den Boo"
},
{
"path": "src/Profiler/view/base/web/js/table.js",
"chars": 2510,
"preview": "define([\n 'jquery',\n 'jquery/jquery.cookie',\n 'jquery/ui',\n 'Mirasvit_Profiler/js/lib/jquery.treetable',\n "
}
]
About this extraction
This page contains the full source code of the mirasvit/module-profiler GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 53 files (144.5 KB), approximately 34.1k tokens, and a symbol index with 146 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.