38 Commits
Author SHA1 Message Date
Andrey Savchenko 1d87865377 Updated readme. 2014-07-11 17:48:19 +03:00
Andrey Savchenko 283f8632df Disabled tooltip animation to fix JS error and UI disappearing from under cursor. 2014-07-11 17:47:54 +03:00
Andrey Savchenko eac2ab5fad Simplified robo task for tooltip to single write. 2014-06-29 00:25:59 +03:00
Andrey Savchenko 26df4f5579 Updated Bootstrap license info in readme. 2014-06-28 23:25:50 +03:00
Andrey Savchenko 83972fdb69 Updated tooltip script. 2014-06-28 23:25:02 +03:00
Andrey Savchenko e42787ec16 Added robo task to generate edited tooltip script from Bootstrap source. 2014-06-28 23:10:48 +03:00
Andrey Savchenko 174b171cdd Refactored less workflow to reference Bootstrap sources. 2014-06-28 22:26:26 +03:00
Andrey Savchenko 2a6c9339d3 Added Robo configuration file for automation. 2014-06-28 22:24:48 +03:00
Andrey Savchenko 103e0c36f5 Committed composer.lock file. 2014-06-28 22:24:05 +03:00
Andrey Savchenko 0e48a61157 Added Bootstrap as development dependency. 2014-06-28 22:23:39 +03:00
Andrey Savchenko 68f8ae42e9 Updated plugin header 2014-05-31 21:36:04 +03:00
Andrey Savchenko f8fa008ec7 Updated links in composer.json for GitHub 2014-05-31 21:32:59 +03:00
Andrey Savchenko b0d376ea8c Updated version in plugin header. 2014-05-24 19:09:54 +03:00
Andrey Savchenko 5cea91d656 Updated readme for 1.2 release. 2014-05-18 23:04:09 +03:00
Andrey Savchenko f568830513 Positioned timeline containers to fix absolute positioning of events inside. 2014-05-18 23:01:09 +03:00
Andrey Savchenko 00ba356da7 Refactored Toolbar event. 2014-05-18 22:41:45 +03:00
Andrey Savchenko 54bb79b202 Updated installation instructions for Packagist. 2014-05-18 22:02:46 +03:00
Andrey Savchenko 334d1cbacc Renamed .hgignore to .gitignore 2014-05-18 22:00:51 +03:00
Tom Nowell 32d8752261 use the core event category rather than admin 2014-04-14 14:36:38 +00:00
Tom Nowell 84eba6e39d Add additional core events 2014-04-12 18:41:11 +00:00
Andrey Savchenko b774292fda Added tag 1.1 for changeset a0de24fa9421 2013-12-11 18:05:59 +02:00
Andrey Savchenko 8c21576f02 Updated version and readme 2013-12-11 18:05:35 +02:00
Andrey Savchenko edb6941eb7 Cleaned up for inspection (coding style, PHPDoc) 2013-12-11 17:47:45 +02:00
Andrey Savchenko 68d6154a5e Switched handling of minimum event width to CSS 2013-12-06 19:17:55 +02:00
Andrey Savchenko 6565cf9cdb Implemented initial HTTP timeline 2013-12-06 00:58:59 +02:00
Andrey Savchenko 247f6f53aa More coding style stuff 2013-12-06 00:15:32 +02:00
Andrey Savchenko 4cc18430da Moved script to normal queue since noConflict was a bust 2013-12-05 23:49:08 +02:00
Andrey Savchenko 0ed37d4d92 Updated for coding style 2013-12-05 23:40:42 +02:00
Andrey Savchenko b7028e1f5a Changed dependency versions to allow minor version updates 2013-12-05 23:35:11 +02:00
Andrey Savchenko 3031ca5ca0 Implemented stacking close queries 2013-12-04 23:18:31 +02:00
Andrey Savchenko e2a176f8ef Switched tooltips implementation to modified Bootstrap script 2013-12-04 22:18:38 +02:00
Andrey Savchenko d8f354afbc Added initial take on SQL queries timeline. 2013-11-30 18:14:36 +02:00
Andrey Savchenko 06c4d5a57a Updates styles with Bootstrap v3 2013-11-27 19:14:33 +02:00
Rarst ea99e8bd35 Tweaked styling for multiple timelines. 2013-06-19 01:21:29 +03:00
Rarst 382f61a78d Clarified it is a plugin in readme, moved screenshot section higher. 2013-04-23 00:47:50 +03:00
Rarst 100411fb7d Added plugin URL and text domain to header. 2013-04-23 00:42:18 +03:00
Rarst 9b7c6e303d Added info about supported stages and frameworks to readme. 2013-04-22 23:45:01 +03:00
Rarst 8d9ff845cf Added tag 1.0 for changeset bf0eefc37428 2013-04-22 23:38:22 +03:00
21 changed files with 1296 additions and 453 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
vendor
-2
View File
@@ -1,2 +0,0 @@
vendor
composer.lock
+51
View File
@@ -0,0 +1,51 @@
<?php
/**
* This is project's console commands configuration for Robo task runner.
*
* @see http://robo.li/
*/
class RoboFile extends \Robo\Tasks {
/**
* Watches for changes.
*/
public function watch() {
$less_callback = function () {
$this->less();
};
$this->taskWatch()
->monitor( 'css/variables.less', $less_callback )
->monitor( 'css/laps.less', $less_callback )
->run();
}
/**
* Compiles stylesheet from less into css.
*/
public function less() {
$this->taskExec( 'lessc css/laps.less css/laps.css' )->run();
}
/**
* Copies tooltip.js from Bootstrap sources and edits plugin name.
*/
public function tooltip() {
$replacements = array(
'+function ($) {' => '// modified to use lapstooltip as plugin name' . "\n\n" . '+function ($) {',
'this.init(\'tooltip\'' => 'this.init(\'lapstooltip\'',
'bs.tooltip' => 'bs.lapstooltip',
'$.fn.tooltip' => '$.fn.lapstooltip',
);
$source = strtr( file_get_contents( 'vendor/twbs/bootstrap/js/tooltip.js' ), $replacements );
$this->taskWriteToFile( 'js/tooltip.js' )
->text( $source )
->run();
}
}
+7 -5
View File
@@ -3,7 +3,7 @@
"description": "Light WordPress profiler.", "description": "Light WordPress profiler.",
"keywords" : ["wordpress", "performance"], "keywords" : ["wordpress", "performance"],
"type" : "wordpress-plugin", "type" : "wordpress-plugin",
"homepage" : "https://bitbucket.org/Rarst/laps", "homepage" : "https://github.com/Rarst/laps",
"license" : "MIT", "license" : "MIT",
"authors" : [ "authors" : [
{ {
@@ -13,15 +13,17 @@
} }
], ],
"support" : { "support" : {
"issues": "https://bitbucket.org/Rarst/laps/issues", "issues": "https://github.com/Rarst/laps/issues"
"source": "https://bitbucket.org/Rarst/laps/src"
}, },
"autoload" : { "autoload" : {
"classmap": ["src/"] "classmap": ["src/"]
}, },
"require" : { "require" : {
"composer/installers": "~1.0", "composer/installers": "~1.0",
"symfony/stopwatch" : "~2.2.0", "symfony/stopwatch" : "~2.4",
"mustache/mustache" : "~2.3.0" "mustache/mustache" : "~2.4"
},
"require-dev": {
"twbs/bootstrap": "~3.2"
} }
} }
Generated
+251
View File
@@ -0,0 +1,251 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "7495421e8df337988c9906e05aab637b",
"packages": [
{
"name": "composer/installers",
"version": "v1.0.15",
"source": {
"type": "git",
"url": "https://github.com/composer/installers.git",
"reference": "8e6a72a78f6cfd537fde3350720dfdc9ae3c5621"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/installers/zipball/8e6a72a78f6cfd537fde3350720dfdc9ae3c5621",
"reference": "8e6a72a78f6cfd537fde3350720dfdc9ae3c5621",
"shasum": ""
},
"replace": {
"roundcube/plugin-installer": "*",
"shama/baton": "*"
},
"require-dev": {
"composer/composer": "1.0.*@dev",
"phpunit/phpunit": "4.1.*"
},
"type": "composer-installer",
"extra": {
"class": "Composer\\Installers\\Installer",
"branch-alias": {
"dev-master": "1.0-dev"
}
},
"autoload": {
"psr-0": {
"Composer\\Installers\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Kyle Robinson Young",
"email": "kyle@dontkry.com",
"homepage": "https://github.com/shama",
"role": "Developer"
}
],
"description": "A multi-framework Composer library installer",
"homepage": "http://composer.github.com/installers/",
"keywords": [
"Craft",
"Dolibarr",
"Hurad",
"MODX Evo",
"OXID",
"WolfCMS",
"agl",
"annotatecms",
"bitrix",
"cakephp",
"codeigniter",
"concrete5",
"croogo",
"drupal",
"elgg",
"fuelphp",
"installer",
"joomla",
"kohana",
"laravel",
"lithium",
"magento",
"mako",
"mediawiki",
"modulework",
"moodle",
"phpbb",
"piwik",
"ppi",
"roundcube",
"shopware",
"silverstripe",
"symfony",
"typo3",
"wordpress",
"zend",
"zikula"
],
"time": "2014-06-23 16:25:28"
},
{
"name": "mustache/mustache",
"version": "v2.6.1",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/mustache.php.git",
"reference": "1430ca884912eaba2b3830856b45685733b16789"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bobthecow/mustache.php/zipball/1430ca884912eaba2b3830856b45685733b16789",
"reference": "1430ca884912eaba2b3830856b45685733b16789",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"require-dev": {
"phpunit/phpunit": "*"
},
"type": "library",
"autoload": {
"psr-0": {
"Mustache": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Justin Hileman",
"email": "justin@justinhileman.info",
"homepage": "http://justinhileman.com"
}
],
"description": "A Mustache implementation in PHP.",
"homepage": "https://github.com/bobthecow/mustache.php",
"keywords": [
"mustache",
"templating"
],
"time": "2014-06-20 19:34:49"
},
{
"name": "symfony/stopwatch",
"version": "v2.5.0",
"target-dir": "Symfony/Component/Stopwatch",
"source": {
"type": "git",
"url": "https://github.com/symfony/Stopwatch.git",
"reference": "724d73604ebe6c1c9bdf36533b556123bd9075a1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/Stopwatch/zipball/724d73604ebe6c1c9bdf36533b556123bd9075a1",
"reference": "724d73604ebe6c1c9bdf36533b556123bd9075a1",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.5-dev"
}
},
"autoload": {
"psr-0": {
"Symfony\\Component\\Stopwatch\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Symfony Community",
"homepage": "http://symfony.com/contributors"
}
],
"description": "Symfony Stopwatch Component",
"homepage": "http://symfony.com",
"time": "2014-04-18 20:40:13"
}
],
"packages-dev": [
{
"name": "twbs/bootstrap",
"version": "v3.2.0",
"source": {
"type": "git",
"url": "https://github.com/twbs/bootstrap.git",
"reference": "c068162161154a4b85110ea1e7dd3d7897ce2b72"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twbs/bootstrap/zipball/c068162161154a4b85110ea1e7dd3d7897ce2b72",
"reference": "c068162161154a4b85110ea1e7dd3d7897ce2b72",
"shasum": ""
},
"replace": {
"twitter/bootstrap": "self.version"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jacob Thornton",
"email": "jacobthornton@gmail.com"
},
{
"name": "Mark Otto",
"email": "markdotto@gmail.com"
}
],
"description": "The most popular front-end framework for developing responsive, mobile first projects on the web.",
"homepage": "http://getbootstrap.com",
"keywords": [
"JS",
"css",
"framework",
"front-end",
"less",
"mobile-first",
"responsive",
"web"
],
"time": "2014-06-26 16:36:48"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"platform": [],
"platform-dev": []
}
-265
View File
@@ -1,265 +0,0 @@
/*! Hint.css - v1.2.1 - 2013-03-24
* http://kushagragour.in/lab/hint/
* Copyright (c) 2013 Kushagra Gour; Licensed MIT */
/*-------------------------------------*\
HINT.css - A CSS tooltip library
\*-------------------------------------*/
/**
* HINT.css is a tooltip library made in pure CSS.
*
* Source: https://github.com/chinchang/hint.css
* Demo: http://kushagragour.in/labs/hint/
*
* Release under The MIT License
*
*/
/**
* source: hint-core.scss
*
* Defines the basic styling for the tooltip.
* Each tooltip is made of 2 parts:
* 1) body (:after)
* 2) arrow (:before)
*
* Classes added:
* 1) hint
*/
.hint, [data-hint] {
position: relative;
display: inline-block;
/**
* tooltip arrow
*/
/**
* tooltip body
*/ }
.hint:before, .hint:after, [data-hint]:before, [data-hint]:after {
position: absolute;
visibility: hidden;
opacity: 0;
z-index: 1000000;
pointer-events: none;
-webkit-transition: 0.3s ease;
-moz-transition: 0.3s ease;
transition: 0.3s ease; }
.hint:hover:before, .hint:hover:after, [data-hint]:hover:before, [data-hint]:hover:after {
visibility: visible;
opacity: 1; }
.hint:before, [data-hint]:before {
content: '';
position: absolute;
background: transparent;
border: 6px solid transparent;
z-index: 1000001; }
.hint:after, [data-hint]:after {
content: attr(data-hint);
background: #383838;
color: white;
text-shadow: 0 -1px 0px black;
padding: 8px 10px;
font-size: 12px;
line-height: 12px;
white-space: nowrap;
box-shadow: 4px 4px 8px rgba(0, 0, 0, 0.3); }
/**
* source: hint-position.scss
*
* Defines the positoning logic for the tooltips.
*
* Classes added:
* 1) hint--top
* 2) hint--bottom
* 3) hint--left
* 4) hint--right
*/
/**
* set default color for tooltip arrows
*/
.hint--top:before {
border-top-color: #383838; }
.hint--bottom:before {
border-bottom-color: #383838; }
.hint--left:before {
border-left-color: #383838; }
.hint--right:before {
border-right-color: #383838; }
/**
* top tooltip
*/
.hint--top:before {
margin-bottom: -12px; }
.hint--top:after {
margin-left: -18px; }
.hint--top:before, .hint--top:after {
bottom: 100%;
left: 50%; }
.hint--top:hover:before, .hint--top:hover:after {
-webkit-transform: translateY(-8px);
-moz-transform: translateY(-8px);
transform: translateY(-8px); }
/**
* bottom tooltip
*/
.hint--bottom:before {
margin-top: -12px; }
.hint--bottom:after {
margin-left: -18px; }
.hint--bottom:before, .hint--bottom:after {
top: 100%;
left: 50%; }
.hint--bottom:hover:before, .hint--bottom:hover:after {
-webkit-transform: translateY(8px);
-moz-transform: translateY(8px);
transform: translateY(8px); }
/**
* right tooltip
*/
.hint--right:before {
margin-left: -12px;
margin-bottom: -6px; }
.hint--right:after {
margin-bottom: -14px; }
.hint--right:before, .hint--right:after {
left: 100%;
bottom: 50%; }
.hint--right:hover:before, .hint--right:hover:after {
-webkit-transform: translateX(8px);
-moz-transform: translateX(8px);
transform: translateX(8px); }
/**
* left tooltip
*/
.hint--left:before {
margin-right: -12px;
margin-bottom: -6px; }
.hint--left:after {
margin-bottom: -14px; }
.hint--left:before, .hint--left:after {
right: 100%;
bottom: 50%; }
.hint--left:hover:before, .hint--left:hover:after {
-webkit-transform: translateX(-8px);
-moz-transform: translateX(-8px);
transform: translateX(-8px); }
/**
* source: hint-color-types.scss
*
* Contains tooltips of various types based on color differences.
*
* Classes added:
* 1) hint--error
* 2) hint--warning
* 3) hint--info
* 4) hint--success
*
*/
/**
* Error
*/
.hint--error:after {
background-color: #b34e4d;
text-shadow: 0 -1px 0px #5a2626; }
.hint--error.hint--top:before {
border-top-color: #b34e4d; }
.hint--error.hint--bottom:before {
border-bottom-color: #b34e4d; }
.hint--error.hint--left:before {
border-left-color: #b34e4d; }
.hint--error.hint--right:before {
border-right-color: #b34e4d; }
/**
* Warning
*/
.hint--warning:after {
background-color: #c09854;
text-shadow: 0 -1px 0px #6d5228; }
.hint--warning.hint--top:before {
border-top-color: #c09854; }
.hint--warning.hint--bottom:before {
border-bottom-color: #c09854; }
.hint--warning.hint--left:before {
border-left-color: #c09854; }
.hint--warning.hint--right:before {
border-right-color: #c09854; }
/**
* Info
*/
.hint--info:after {
background-color: #3986ac;
text-shadow: 0 -1px 0px #193c4c; }
.hint--info.hint--top:before {
border-top-color: #3986ac; }
.hint--info.hint--bottom:before {
border-bottom-color: #3986ac; }
.hint--info.hint--left:before {
border-left-color: #3986ac; }
.hint--info.hint--right:before {
border-right-color: #3986ac; }
/**
* Success
*/
.hint--success:after {
background-color: #458746;
text-shadow: 0 -1px 0px #1a331a; }
.hint--success.hint--top:before {
border-top-color: #458746; }
.hint--success.hint--bottom:before {
border-bottom-color: #458746; }
.hint--success.hint--left:before {
border-left-color: #458746; }
.hint--success.hint--right:before {
border-right-color: #458746; }
/**
* source: hint-always.scss
*
* Defines a persisted tooltip which shows always.
*
* Classes added:
* 1) hint--always
*
*/
.hint--always:after, .hint--always:before {
opacity: 1;
visibility: visible; }
.hint--always.hint--top:after, .hint--always.hint--top:before {
-webkit-transform: translateY(-8px);
-moz-transform: translateY(-8px);
transform: translateY(-8px); }
.hint--always.hint--bottom:after, .hint--always.hint--bottom:before {
-webkit-transform: translateY(8px);
-moz-transform: translateY(8px);
transform: translateY(8px); }
.hint--always.hint--left:after, .hint--always.hint--left:before {
-webkit-transform: translateX(-8px);
-moz-transform: translateX(-8px);
transform: translateX(-8px); }
.hint--always.hint--right:after, .hint--always.hint--right:before {
-webkit-transform: translateX(8px);
-moz-transform: translateX(8px);
transform: translateX(8px); }
/**
* source: hint-rounded.scss
*
* Defines rounded corner tooltips.
*
* Classes added:
* 1) hint--rounded
*
*/
.hint--rounded:after {
border-radius: 4px; }
+137 -62
View File
@@ -4,6 +4,9 @@
position: fixed; position: fixed;
left: 5%; left: 5%;
} }
#wpadminbar #wp-admin-bar-laps .ab-submenu {
padding: 0;
}
#wpadminbar #wp-admin-bar-laps_output { #wpadminbar #wp-admin-bar-laps_output {
padding: 0 10px; padding: 0 10px;
} }
@@ -16,89 +19,161 @@
#wpadminbar .laps-timeline { #wpadminbar .laps-timeline {
overflow: hidden; overflow: hidden;
height: 20px; height: 20px;
background-color: #f7f7f7; margin-bottom: 20px;
background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9); background-color: #f5f5f5;
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
-moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px; border-radius: 4px;
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
margin: 6px 0;
position: relative;
} }
#wpadminbar .laps-timeline .event { #wpadminbar .laps-timeline .event {
width: 0%;
height: 20px;
color: #fff;
float: left; float: left;
width: 0%;
height: 100%;
font-size: 12px; font-size: 12px;
line-height: 20px;
color: #ffffff;
text-align: center; text-align: center;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); background-color: #5bc0de;
background-color: #dd514c;
background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));
background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
-moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: width 0.6s ease; -webkit-transition: width 0.6s ease;
-moz-transition: width 0.6s ease;
-o-transition: width 0.6s ease; -o-transition: width 0.6s ease;
transition: width 0.6s ease; transition: width 0.6s ease;
position: absolute; position: absolute;
border-left: 1px solid #f5f5f5;
min-width: 2px;
} }
#wpadminbar .laps-timeline .event + .event { #wpadminbar .laps-timeline .event[aria-valuenow="1"],
-webkit-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); #wpadminbar .laps-timeline .event[aria-valuenow="2"] {
-moz-box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); min-width: 30px;
box-shadow: inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15); }
#wpadminbar .laps-timeline .event[aria-valuenow="0"] {
color: #777777;
min-width: 30px;
background-color: transparent;
background-image: none;
box-shadow: none;
} }
#wpadminbar .laps-timeline .event-theme { #wpadminbar .laps-timeline .event-theme {
background-color: #5eb95e; background-color: #5cb85c;
background-image: -moz-linear-gradient(top, #62c462, #57a957);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));
background-image: -webkit-linear-gradient(top, #62c462, #57a957);
background-image: -o-linear-gradient(top, #62c462, #57a957);
background-image: linear-gradient(to bottom, #62c462, #57a957);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);
} }
#wpadminbar .laps-timeline .event-plugin { #wpadminbar .laps-timeline .event-plugin {
background-color: #4bb1cf; background-color: #428bca;
background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));
background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);
} }
#wpadminbar .laps-timeline .event-core { #wpadminbar .laps-timeline .event-core {
background-color: #faa732; background-color: #f0ad4e;
background-image: -moz-linear-gradient(top, #fbb450, #f89406);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fbb450), to(#f89406));
background-image: -webkit-linear-gradient(top, #fbb450, #f89406);
background-image: -o-linear-gradient(top, #fbb450, #f89406);
background-image: linear-gradient(to bottom, #fbb450, #f89406);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89406', GradientType=0);
} }
#wpadminbar [data-hint]:after { #wpadminbar .laps-timeline .event-query {
font-size: 13px; background-color: #5bc0de;
}
#wpadminbar .laps-timeline .event-http {
background-color: #d9534f;
}
#wpadminbar .tooltip {
position: absolute;
z-index: 100000;
display: block;
visibility: visible;
font-size: 12px;
line-height: 1.4;
opacity: 0;
filter: alpha(opacity=0);
}
#wpadminbar .tooltip.in {
opacity: 0.9;
filter: alpha(opacity=90);
}
#wpadminbar .tooltip.top {
margin-top: -3px;
padding: 5px 0;
}
#wpadminbar .tooltip.right {
margin-left: 3px;
padding: 0 5px;
}
#wpadminbar .tooltip.bottom {
margin-top: 3px;
padding: 5px 0;
}
#wpadminbar .tooltip.left {
margin-left: -3px;
padding: 0 5px;
}
#wpadminbar .tooltip-inner {
max-width: 300px;
padding: 3px 8px;
color: #cccccc;
text-align: center;
text-decoration: none;
background-color: #464646; background-color: #464646;
text-shadow: none; border-radius: 4px;
} }
#wpadminbar .hint--bottom:before { #wpadminbar .tooltip-arrow {
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
}
#wpadminbar .tooltip.top .tooltip-arrow {
bottom: 0;
left: 50%;
margin-left: -5px;
border-width: 5px 5px 0;
border-top-color: #464646;
}
#wpadminbar .tooltip.top-left .tooltip-arrow {
bottom: 0;
left: 5px;
border-width: 5px 5px 0;
border-top-color: #464646;
}
#wpadminbar .tooltip.top-right .tooltip-arrow {
bottom: 0;
right: 5px;
border-width: 5px 5px 0;
border-top-color: #464646;
}
#wpadminbar .tooltip.right .tooltip-arrow {
top: 50%;
left: 0;
margin-top: -5px;
border-width: 5px 5px 5px 0;
border-right-color: #464646;
}
#wpadminbar .tooltip.left .tooltip-arrow {
top: 50%;
right: 0;
margin-top: -5px;
border-width: 5px 0 5px 5px;
border-left-color: #464646;
}
#wpadminbar .tooltip.bottom .tooltip-arrow {
top: 0;
left: 50%;
margin-left: -5px;
border-width: 0 5px 5px;
border-bottom-color: #464646; border-bottom-color: #464646;
} }
#wpadminbar .tooltip.bottom-left .tooltip-arrow {
top: 0;
left: 5px;
border-width: 0 5px 5px;
border-bottom-color: #464646;
}
#wpadminbar .tooltip.bottom-right .tooltip-arrow {
top: 0;
right: 5px;
border-width: 0 5px 5px;
border-bottom-color: #464646;
}
#wpadminbar .tooltip.in {
opacity: 1;
filter: alpha(opacity=100);
}
#wpadminbar .tooltip-inner {
line-height: 20px;
text-align: left;
}
+39 -41
View File
@@ -1,15 +1,24 @@
@import "mixins.less"; @import (reference) "../vendor/twbs/bootstrap/less/variables.less";
@import (reference) "../vendor/twbs/bootstrap/less/mixins.less";
@import (reference) "../vendor/twbs/bootstrap/less/progress-bars.less";
@import "variables.less";
#wpadminbar { #wpadminbar {
#wp-admin-bar-laps .ab-sub-wrapper { #wp-admin-bar-laps {
// display: block !important;
.ab-sub-wrapper {
min-width: 90%; min-width: 90%;
width: 90%; width: 90%;
position: fixed; position: fixed;
left: 5%; left: 5%;
} }
.ab-submenu {
padding: 0;
}
}
#wp-admin-bar-laps_output { #wp-admin-bar-laps_output {
padding: 0 10px; padding: 0 10px;
@@ -21,57 +30,46 @@
.ab-empty-item { .ab-empty-item {
display: none; display: none;
} }
} }
.laps-timeline { .laps-timeline {
overflow: hidden; .progress;
height: 20px; margin: 6px 0;
#gradient > .vertical(#f5f5f5, #f9f9f9); position: relative;
.box-shadow(inset 0 1px 2px rgba(0,0,0,.1));
.border-radius(4px); .event {
.progress-bar;
position: absolute; // disable stacking at start
border-left: 1px solid @timeline-bg;
min-width: 2px;
} }
.laps-timeline .event { .event-theme {
width: 0%; background-color: @event-theme-color;
height: 20px;
color: #fff;
float: left;
font-size: 12px;
text-align: center;
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
#gradient > .vertical(#ee5f5b, #c43c35);
.box-shadow(inset 0 -1px 0 rgba(0,0,0,.15));
.box-sizing(border-box);
.transition(width .6s ease);
position: absolute;
} }
.event-plugin {
.laps-timeline .event + .event { background-color: @event-plugin-color;
.box-shadow(~"inset 1px 0 0 rgba(0,0,0,.15), inset 0 -1px 0 rgba(0,0,0,.15)");
} }
.event-core {
.laps-timeline .event-theme { background-color: @event-core-color;
#gradient > .vertical(#62c462, #57a957);
} }
.event-query {
.laps-timeline .event-plugin { background-color: @event-info-color;
#gradient > .vertical(#5bc0de, #339bb9);
} }
.event-http {
background-color: @event-warning-color;
}
}
.laps-timeline .event-core { @import "../vendor/twbs/bootstrap/less/tooltip.less";
#gradient > .vertical(lighten(#f89406, 15%), #f89406);
}
[data-hint]:after { .tooltip.in {
font-size: 13px; .opacity(1);
background-color: #464646;
text-shadow: none;
} }
.hint--bottom:before { .tooltip-inner {
border-bottom-color: #464646; line-height: 20px;
text-align: left;
} }
} }
-38
View File
@@ -1,38 +0,0 @@
.transition(@transition) {
-webkit-transition: @transition;
-moz-transition: @transition;
-o-transition: @transition;
transition: @transition;
}
.box-sizing(@boxmodel) {
-webkit-box-sizing: @boxmodel;
-moz-box-sizing: @boxmodel;
box-sizing: @boxmodel;
}
.border-radius(@radius) {
-webkit-border-radius: @radius;
-moz-border-radius: @radius;
border-radius: @radius;
}
.box-shadow(@shadow) {
-webkit-box-shadow: @shadow;
-moz-box-shadow: @shadow;
box-shadow: @shadow;
}
#gradient {
.vertical(@startColor: #555, @endColor: #333) {
background-color: mix(@startColor, @endColor, 60%);
background-image: -moz-linear-gradient(top, @startColor, @endColor); // FF 3.6+
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(@startColor), to(@endColor)); // Safari 4+, Chrome 2+
background-image: -webkit-linear-gradient(top, @startColor, @endColor); // Safari 5.1+, Chrome 10+
background-image: -o-linear-gradient(top, @startColor, @endColor); // Opera 11.10
background-image: linear-gradient(to bottom, @startColor, @endColor); // Standard, IE10
background-repeat: repeat-x;
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",argb(@startColor), argb(@endColor))); // IE9 and down
}
}
+21
View File
@@ -0,0 +1,21 @@
@font-size-small: 12px;
@line-height-computed: 20px;
// colors
@timeline-bg: #f5f5f5;
@event-theme-color: #5cb85c;
@event-plugin-color: #428bca;
@event-core-color: #f0ad4e;
@event-info-color: #5bc0de;
@event-warning-color: #d9534f;
@progress-bg: @timeline-bg;
@progress-bar-bg: @event-info-color;
// because admin bar is 99999
@zindex-tooltip: 100000;
@tooltip-max-width: 300px;
@tooltip-color: #ccc;
@tooltip-bg: #464646;
@tooltip-arrow-width: 5px;
@tooltip-arrow-color: @tooltip-bg;
@border-radius-base: 4px;
+459
View File
@@ -0,0 +1,459 @@
/* ========================================================================
* Bootstrap: tooltip.js v3.2.0
* http://getbootstrap.com/javascript/#tooltip
* Inspired by the original jQuery.tipsy by Jason Frame
* ========================================================================
* Copyright 2011-2014 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* ======================================================================== */
// modified to use lapstooltip as plugin name
+function ($) {
'use strict';
// TOOLTIP PUBLIC CLASS DEFINITION
// ===============================
var Tooltip = function (element, options) {
this.type =
this.options =
this.enabled =
this.timeout =
this.hoverState =
this.$element = null
this.init('lapstooltip', element, options)
}
Tooltip.VERSION = '3.2.0'
Tooltip.DEFAULTS = {
animation: true,
placement: 'top',
selector: false,
template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
trigger: 'hover focus',
title: '',
delay: 0,
html: false,
container: false,
viewport: {
selector: 'body',
padding: 0
}
}
Tooltip.prototype.init = function (type, element, options) {
this.enabled = true
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
var triggers = this.options.trigger.split(' ')
for (var i = triggers.length; i--;) {
var trigger = triggers[i]
if (trigger == 'click') {
this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
} else if (trigger != 'manual') {
var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
}
}
this.options.selector ?
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
this.fixTitle()
}
Tooltip.prototype.getDefaults = function () {
return Tooltip.DEFAULTS
}
Tooltip.prototype.getOptions = function (options) {
options = $.extend({}, this.getDefaults(), this.$element.data(), options)
if (options.delay && typeof options.delay == 'number') {
options.delay = {
show: options.delay,
hide: options.delay
}
}
return options
}
Tooltip.prototype.getDelegateOptions = function () {
var options = {}
var defaults = this.getDefaults()
this._options && $.each(this._options, function (key, value) {
if (defaults[key] != value) options[key] = value
})
return options
}
Tooltip.prototype.enter = function (obj) {
var self = obj instanceof this.constructor ?
obj : $(obj.currentTarget).data('bs.' + this.type)
if (!self) {
self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
$(obj.currentTarget).data('bs.' + this.type, self)
}
clearTimeout(self.timeout)
self.hoverState = 'in'
if (!self.options.delay || !self.options.delay.show) return self.show()
self.timeout = setTimeout(function () {
if (self.hoverState == 'in') self.show()
}, self.options.delay.show)
}
Tooltip.prototype.leave = function (obj) {
var self = obj instanceof this.constructor ?
obj : $(obj.currentTarget).data('bs.' + this.type)
if (!self) {
self = new this.constructor(obj.currentTarget, this.getDelegateOptions())
$(obj.currentTarget).data('bs.' + this.type, self)
}
clearTimeout(self.timeout)
self.hoverState = 'out'
if (!self.options.delay || !self.options.delay.hide) return self.hide()
self.timeout = setTimeout(function () {
if (self.hoverState == 'out') self.hide()
}, self.options.delay.hide)
}
Tooltip.prototype.show = function () {
var e = $.Event('show.bs.' + this.type)
if (this.hasContent() && this.enabled) {
this.$element.trigger(e)
var inDom = $.contains(document.documentElement, this.$element[0])
if (e.isDefaultPrevented() || !inDom) return
var that = this
var $tip = this.tip()
var tipId = this.getUID(this.type)
this.setContent()
$tip.attr('id', tipId)
this.$element.attr('aria-describedby', tipId)
if (this.options.animation) $tip.addClass('fade')
var placement = typeof this.options.placement == 'function' ?
this.options.placement.call(this, $tip[0], this.$element[0]) :
this.options.placement
var autoToken = /\s?auto?\s?/i
var autoPlace = autoToken.test(placement)
if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
$tip
.detach()
.css({ top: 0, left: 0, display: 'block' })
.addClass(placement)
.data('bs.' + this.type, this)
this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
var pos = this.getPosition()
var actualWidth = $tip[0].offsetWidth
var actualHeight = $tip[0].offsetHeight
if (autoPlace) {
var orgPlacement = placement
var $parent = this.$element.parent()
var parentDim = this.getPosition($parent)
placement = placement == 'bottom' && pos.top + pos.height + actualHeight - parentDim.scroll > parentDim.height ? 'top' :
placement == 'top' && pos.top - parentDim.scroll - actualHeight < 0 ? 'bottom' :
placement == 'right' && pos.right + actualWidth > parentDim.width ? 'left' :
placement == 'left' && pos.left - actualWidth < parentDim.left ? 'right' :
placement
$tip
.removeClass(orgPlacement)
.addClass(placement)
}
var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
this.applyPlacement(calculatedOffset, placement)
var complete = function () {
that.$element.trigger('shown.bs.' + that.type)
that.hoverState = null
}
$.support.transition && this.$tip.hasClass('fade') ?
$tip
.one('bsTransitionEnd', complete)
.emulateTransitionEnd(150) :
complete()
}
}
Tooltip.prototype.applyPlacement = function (offset, placement) {
var $tip = this.tip()
var width = $tip[0].offsetWidth
var height = $tip[0].offsetHeight
// manually read margins because getBoundingClientRect includes difference
var marginTop = parseInt($tip.css('margin-top'), 10)
var marginLeft = parseInt($tip.css('margin-left'), 10)
// we must check for NaN for ie 8/9
if (isNaN(marginTop)) marginTop = 0
if (isNaN(marginLeft)) marginLeft = 0
offset.top = offset.top + marginTop
offset.left = offset.left + marginLeft
// $.fn.offset doesn't round pixel values
// so we use setOffset directly with our own function B-0
$.offset.setOffset($tip[0], $.extend({
using: function (props) {
$tip.css({
top: Math.round(props.top),
left: Math.round(props.left)
})
}
}, offset), 0)
$tip.addClass('in')
// check to see if placing tip in new offset caused the tip to resize itself
var actualWidth = $tip[0].offsetWidth
var actualHeight = $tip[0].offsetHeight
if (placement == 'top' && actualHeight != height) {
offset.top = offset.top + height - actualHeight
}
var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
if (delta.left) offset.left += delta.left
else offset.top += delta.top
var arrowDelta = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
var arrowPosition = delta.left ? 'left' : 'top'
var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight'
$tip.offset(offset)
this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
}
Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
}
Tooltip.prototype.setContent = function () {
var $tip = this.tip()
var title = this.getTitle()
$tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
$tip.removeClass('fade in top bottom left right')
}
Tooltip.prototype.hide = function () {
var that = this
var $tip = this.tip()
var e = $.Event('hide.bs.' + this.type)
this.$element.removeAttr('aria-describedby')
function complete() {
if (that.hoverState != 'in') $tip.detach()
that.$element.trigger('hidden.bs.' + that.type)
}
this.$element.trigger(e)
if (e.isDefaultPrevented()) return
$tip.removeClass('in')
$.support.transition && this.$tip.hasClass('fade') ?
$tip
.one('bsTransitionEnd', complete)
.emulateTransitionEnd(150) :
complete()
this.hoverState = null
return this
}
Tooltip.prototype.fixTitle = function () {
var $e = this.$element
if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') {
$e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
}
}
Tooltip.prototype.hasContent = function () {
return this.getTitle()
}
Tooltip.prototype.getPosition = function ($element) {
$element = $element || this.$element
var el = $element[0]
var isBody = el.tagName == 'BODY'
return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, {
scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(),
width: isBody ? $(window).width() : $element.outerWidth(),
height: isBody ? $(window).height() : $element.outerHeight()
}, isBody ? { top: 0, left: 0 } : $element.offset())
}
Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
/* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
}
Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
var delta = { top: 0, left: 0 }
if (!this.$viewport) return delta
var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
var viewportDimensions = this.getPosition(this.$viewport)
if (/right|left/.test(placement)) {
var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll
var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
if (topEdgeOffset < viewportDimensions.top) { // top overflow
delta.top = viewportDimensions.top - topEdgeOffset
} else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset
}
} else {
var leftEdgeOffset = pos.left - viewportPadding
var rightEdgeOffset = pos.left + viewportPadding + actualWidth
if (leftEdgeOffset < viewportDimensions.left) { // left overflow
delta.left = viewportDimensions.left - leftEdgeOffset
} else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
}
}
return delta
}
Tooltip.prototype.getTitle = function () {
var title
var $e = this.$element
var o = this.options
title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
return title
}
Tooltip.prototype.getUID = function (prefix) {
do prefix += ~~(Math.random() * 1000000)
while (document.getElementById(prefix))
return prefix
}
Tooltip.prototype.tip = function () {
return (this.$tip = this.$tip || $(this.options.template))
}
Tooltip.prototype.arrow = function () {
return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow'))
}
Tooltip.prototype.validate = function () {
if (!this.$element[0].parentNode) {
this.hide()
this.$element = null
this.options = null
}
}
Tooltip.prototype.enable = function () {
this.enabled = true
}
Tooltip.prototype.disable = function () {
this.enabled = false
}
Tooltip.prototype.toggleEnabled = function () {
this.enabled = !this.enabled
}
Tooltip.prototype.toggle = function (e) {
var self = this
if (e) {
self = $(e.currentTarget).data('bs.' + this.type)
if (!self) {
self = new this.constructor(e.currentTarget, this.getDelegateOptions())
$(e.currentTarget).data('bs.' + this.type, self)
}
}
self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
}
Tooltip.prototype.destroy = function () {
clearTimeout(this.timeout)
this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
}
// TOOLTIP PLUGIN DEFINITION
// =========================
function Plugin(option) {
return this.each(function () {
var $this = $(this)
var data = $this.data('bs.lapstooltip')
var options = typeof option == 'object' && option
if (!data && option == 'destroy') return
if (!data) $this.data('bs.lapstooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]()
})
}
var old = $.fn.lapstooltip
$.fn.lapstooltip = Plugin
$.fn.lapstooltip.Constructor = Tooltip
// TOOLTIP NO CONFLICT
// ===================
$.fn.lapstooltip.noConflict = function () {
$.fn.lapstooltip = old
return this
}
}(jQuery);
+3 -3
View File
@@ -1,12 +1,12 @@
<?php <?php
/* /*
Plugin Name: Laps Plugin Name: Laps
Plugin URI: Plugin URI: https://github.com/Rarst/laps
Description: Light WordPress profiler. Description: Light WordPress profiler.
Author: Andrey "Rarst" Savchenko Author: Andrey "Rarst" Savchenko
Version: 1.0 Version:
Author URI: http://www.rarst.net/ Author URI: http://www.rarst.net/
Text Domain: Text Domain: laps
Domain Path: /lang Domain Path: /lang
License: MIT License: MIT
+39 -9
View File
@@ -1,28 +1,59 @@
# Description # Laps — light WP profiler
Laps is light WordPress profiler, which aims to be: Laps is light WordPress profiler (in a plugin), which aims to be:
- always on and zero click (just hover on toolbar entry) away - always on and zero click (just hover on toolbar entry) away
- quick indicator of what hogs page in general or right now - quick indicator of what hogs page in general or right now
It is no match or replacement for *real* profiler, but is friendly and cute. It is no match or replacement for *real* profiler, but is friendly and cute.
# Installation Out of the box Laps supports common stage of WordPress page life cycle:
Laps is a Composer package and can be installed via: - plugins load
- themes load
- core init
- main loop
php composer.phar create-project rarst/laps --repository-url=http://rarst.net And some of third party hooks conventions for themes using:
# Frequently Asked Questions - Theme Hook Alliance
- Hybrid
- Genesis
- Thematic
None... Yet. There are also additional optional timelines, displaying:
- SQL queries (with `SAVEQUERIES` constant enabled)
- HTTP requests performed
# Screenshot # Screenshot
![Laps screenshot](http://i.imgur.com/zFokmkU.png) ![Laps screenshot](http://i.imgur.com/zFokmkU.png)
# Installation
Laps is a Composer package and can be installed in plugin directory via:
composer create-project rarst/laps --no-dev
# Changelog # Changelog
## 1.2.1
- disabled tooltip animation to fix JS errors and UI disappearing from under cursor
## 1.2
- merged RP with additional core events in admin
- implemented (ballpark) toolbar event
- fixed issue with event offsets in timelines
## 1.1
- added SQL timeline
- added HTTP timeline
- switched tooltips implementation to Bootstrap script
## 1.0 ## 1.0
- Initial public release - Initial public release
@@ -34,5 +65,4 @@ Laps own code is licensed under MIT and it makes use of code from:
- Composer (MIT) - Composer (MIT)
- Symfony Stopwatch (MIT) - Symfony Stopwatch (MIT)
- Mustache.php (MIT) - Mustache.php (MIT)
- Twitter Bootstrap (Apache License 2.0) - Twitter Bootstrap (MIT)
- Hint.css (MIT)
+43
View File
@@ -2,8 +2,14 @@
namespace Rarst\Laps; namespace Rarst\Laps;
/**
* Main set of events for WordPress core
*/
class Core_Events extends Laps_Events { class Core_Events extends Laps_Events {
/**
* @return array
*/
public function get_events() { public function get_events() {
$stops = array(); $stops = array();
@@ -36,6 +42,32 @@ class Core_Events extends Laps_Events {
'category' => 'core', 'category' => 'core',
); );
$stops['admin_init'][- 1] = array(
'event' => 'Core Admin Init',
'category' => 'core',
);
$stops['admin_init'][ PHP_INT_MAX -1 ] = array(
'action' => 'stop',
'event' => 'Core Admin Init',
'category' => 'core',
);
$stops['_admin_menu'][- 1] = array(
'event' => 'Admin Menu',
'category' => 'core',
);
$stops['admin_menu'][ PHP_INT_MAX -1 ] = array(
'action' => 'stop',
'event' => 'Admin Menu',
'category' => 'core',
);
$stops['admin_bar_menu'][-1] = array(
'event' => 'Toolbar',
'category' => 'core',
);
$stops['loop_start'][10] = array( $stops['loop_start'][10] = array(
'event' => 'Main Loop', 'event' => 'Main Loop',
'category' => 'theme', 'category' => 'theme',
@@ -47,6 +79,17 @@ class Core_Events extends Laps_Events {
'category' => 'theme', 'category' => 'theme',
); );
$stops['admin_enqueue_scripts'][10] = array(
'event' => 'Admin Print Scripts',
'category' => 'core',
);
$stops['admin_print_scripts'][10] = array(
'action' => 'stop',
'event' => 'Admin Print Scripts',
'category' => 'core',
);
return $stops; return $stops;
} }
} }
+21 -11
View File
@@ -2,9 +2,28 @@
namespace Rarst\Laps; namespace Rarst\Laps;
/**
* Base class for bulk event providers
*/
abstract class Laps_Events { abstract class Laps_Events {
/** /**
* Retrieve array of event data
*
* @return array
*/
public function get() {
if ( $this->is_applicable() ) {
return $this->get_events();
}
return array();
}
/**
* Is event pack relevant to current runtime
*
* @return bool * @return bool
*/ */
protected function is_applicable() { protected function is_applicable() {
@@ -13,17 +32,8 @@ abstract class Laps_Events {
} }
/** /**
* @return array * Inherit with internal events compiling and return
*/ *
public function get() {
if( $this->is_applicable() )
return $this->get_events();
return array();
}
/**
* @return array() * @return array()
*/ */
abstract protected function get_events(); abstract protected function get_events();
+11
View File
@@ -2,13 +2,24 @@
namespace Rarst\Laps; namespace Rarst\Laps;
/**
* Events for Genesis Framework based themes
*
* @link http://my.studiopress.com/themes/genesis/
*/
class Genesis_Events extends Laps_Events { class Genesis_Events extends Laps_Events {
/**
* @return bool
*/
protected function is_applicable() { protected function is_applicable() {
return function_exists( 'genesis' ); return function_exists( 'genesis' );
} }
/**
* @return array
*/
protected function get_events() { protected function get_events() {
$events = array(); $events = array();
+13 -1
View File
@@ -2,13 +2,24 @@
namespace Rarst\Laps; namespace Rarst\Laps;
/**
* Events for Hybrid Core based themes
*
* @link http://themehybrid.com/hybrid-core
*/
class Hybrid_Events extends Laps_Events { class Hybrid_Events extends Laps_Events {
/**
* @return bool
*/
protected function is_applicable() { protected function is_applicable() {
return defined( 'HYBRID_VERSION' ); return defined( 'HYBRID_VERSION' );
} }
/**
* @return array
*/
protected function get_events() { protected function get_events() {
$events = array(); $events = array();
@@ -27,7 +38,7 @@ class Hybrid_Events extends Laps_Events {
$sidebars = get_theme_support( 'hybrid-core-sidebars' ); $sidebars = get_theme_support( 'hybrid-core-sidebars' );
if ( ! empty( $sidebars[0] ) ) if ( ! empty( $sidebars[0] ) ) {
foreach ( $sidebars[0] as $sidebar ) { foreach ( $sidebars[0] as $sidebar ) {
$events["{$prefix}_before_{$sidebar}"][10] = array( $events["{$prefix}_before_{$sidebar}"][10] = array(
@@ -41,6 +52,7 @@ class Hybrid_Events extends Laps_Events {
'category' => 'theme', 'category' => 'theme',
); );
} }
}
return $events; return $events;
} }
+161 -15
View File
@@ -12,14 +12,16 @@ class Laps {
/** @var Stopwatch $stopwatch */ /** @var Stopwatch $stopwatch */
public static $stopwatch; public static $stopwatch;
public static $events = array(); public static $events = array();
public static $query_starts = array();
/** /**
* Start Stopwatch and timing plugin load immediately, then set up core events and needed hooks. * Start Stopwatch and timing plugin load immediately, then set up core events and needed hooks.
*/ */
static function on_load() { static function on_load() {
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return; return;
}
self::$stopwatch = new Stopwatch(); self::$stopwatch = new Stopwatch();
self::$stopwatch->start( 'Plugins Load', 'plugin' ); self::$stopwatch->start( 'Plugins Load', 'plugin' );
@@ -28,8 +30,14 @@ class Laps {
add_action( 'pre_update_option_active_plugins', array( __CLASS__, 'pre_update_option_active_plugins' ) ); add_action( 'pre_update_option_active_plugins', array( __CLASS__, 'pre_update_option_active_plugins' ) );
add_action( 'pre_update_site_option_active_sitewide_plugins', array( __CLASS__, 'pre_update_option_active_plugins' ) ); add_action( 'pre_update_site_option_active_sitewide_plugins', array( __CLASS__, 'pre_update_option_active_plugins' ) );
add_action( 'pre_http_request', array( __CLASS__, 'pre_http_request' ), 10, 3 );
add_action( 'http_api_debug', array( __CLASS__, 'http_api_debug' ), 10, 5 );
add_action( 'after_setup_theme', array( __CLASS__, 'after_setup_theme' ), 15 ); add_action( 'after_setup_theme', array( __CLASS__, 'after_setup_theme' ), 15 );
add_action( 'init', array( __CLASS__, 'init' ) ); add_action( 'init', array( __CLASS__, 'init' ) );
if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
add_filter( 'query', array( __CLASS__, 'query' ), 20 );
}
} }
/** /**
@@ -86,16 +94,73 @@ class Laps {
$filter = current_filter(); $filter = current_filter();
$priority = key( $wp_filter[$filter] ); $priority = key( $wp_filter[$filter] );
$event = wp_parse_args( self::$events[$filter][$priority], array( $event = wp_parse_args(
self::$events[$filter][$priority],
array(
'action' => 'start', 'action' => 'start',
'category' => null, 'category' => null,
) ); )
);
self::$stopwatch->$event['action']( $event['event'], $event['category'] ); self::$stopwatch->$event['action']( $event['event'], $event['category'] );
return $input; return $input;
} }
/**
* Capture SQL queries start times
*
* @param string $query
*
* @return string
*/
static function query( $query ) {
global $wpdb;
if ( empty( self::$query_starts ) && ! empty( $wpdb->queries ) ) {
self::$query_starts[count( $wpdb->queries )] = microtime( true ) * 1000;
} else {
self::$query_starts[] = microtime( true ) * 1000;
}
return $query;
}
/**
* Capture start time of HTTP request
*
* @param boolean $false
* @param array $args
* @param string $url
*
* @return boolean
*/
static function pre_http_request( $false, $args, $url ) {
self::$stopwatch->start( $url, 'http' );
return $false;
}
/**
* Capture end time of HTTP request
*
* @param array|\WP_Error $response
* @param string $type
* @param object $class
* @param array $args
* @param string $url
*
* @return mixed
*/
static function http_api_debug( $response, $type, $class, $args, $url ) {
self::$stopwatch->stop( $url );
return $response;
}
/** /**
* When theme is done possibly add theme-specific events. * When theme is done possibly add theme-specific events.
*/ */
@@ -115,16 +180,35 @@ class Laps {
add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) ); add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) );
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) ); add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scripts' ) );
add_action( 'admin_bar_menu', array( __CLASS__, 'admin_bar_menu' ), 100 ); add_action( 'admin_bar_menu', array( __CLASS__, 'admin_bar_menu' ), 100 );
add_action( 'wp_print_footer_scripts', array( __CLASS__, 'print_footer_scripts' ), 11 );
add_action( 'admin_print_footer_scripts', array( __CLASS__, 'print_footer_scripts' ), 11 );
} }
static function enqueue_scripts() { static function enqueue_scripts() {
wp_register_style( 'laps-hint', plugins_url( 'css/hint.css', __DIR__ ), array(), '1.2.1' ); wp_register_script( 'laps-tooltip', plugins_url( 'js/tooltip.js', __DIR__ ), array( 'jquery' ), '3.0.2', true );
wp_register_style( 'laps', plugins_url( 'css/laps.css', __DIR__ ), array( 'laps-hint' ) ); wp_register_style( 'laps', plugins_url( 'css/laps.css', __DIR__ ) );
if ( is_admin_bar_showing() ) if ( is_admin_bar_showing() ) {
wp_enqueue_script( 'laps-tooltip' );
wp_enqueue_style( 'laps' ); wp_enqueue_style( 'laps' );
} }
}
static function print_footer_scripts() {
if ( wp_script_is( 'laps-tooltip', 'done' ) ) {
?>
<script type="text/javascript">
jQuery(document).ready(function () {
jQuery(".laps-timeline .event").lapstooltip({
container: '#wpadminbar', placement: 'bottom', html: true, animation: false
// , trigger: 'click'
});
});
</script><?php
}
}
/** /**
* Render interface and add to the toolbar. * Render interface and add to the toolbar.
@@ -133,20 +217,26 @@ class Laps {
*/ */
static function admin_bar_menu( $wp_admin_bar ) { static function admin_bar_menu( $wp_admin_bar ) {
if ( ! apply_filters( 'laps_can_see', current_user_can( 'manage_options' ) ) ) if ( ! apply_filters( 'laps_can_see', current_user_can( 'manage_options' ) ) ) {
return; return;
}
global $timestart; global $timestart, $wpdb;
$mustache = new \Mustache_Engine( array( $mustache = new \Mustache_Engine(
array(
'loader' => new \Mustache_Loader_FilesystemLoader( dirname( __DIR__ ) . '/views' ), 'loader' => new \Mustache_Loader_FilesystemLoader( dirname( __DIR__ ) . '/views' ),
) ); )
);
self::$stopwatch->stop( 'Toolbar' );
$events = self::$stopwatch->getSectionEvents( '__root__' ); $events = self::$stopwatch->getSectionEvents( '__root__' );
$start = $timestart * 1000; $start = $timestart * 1000;
$end = microtime( true ) * 1000; $end = microtime( true ) * 1000;
$total = $end - $start; $total = $end - $start;
$event_data = array(); $event_data = array();
$http_data = array();
foreach ( $events as $name => $event ) { foreach ( $events as $name => $event ) {
@@ -154,22 +244,78 @@ class Laps {
$duration = $event->getDuration(); $duration = $event->getDuration();
$width = round( $duration / $total * 100, 2 ); $width = round( $duration / $total * 100, 2 );
$category = $event->getCategory(); $category = $event->getCategory();
if ( 'http' == $category ) {
$http_data[] = compact( 'name', 'offset', 'duration', 'width', 'category' );
continue;
}
$memory = $event->getMemory() / 1024 / 1024; $memory = $event->getMemory() / 1024 / 1024;
$event_data[] = compact( 'name', 'offset', 'duration', 'width', 'category', 'memory' ); $event_data[] = compact( 'name', 'offset', 'duration', 'width', 'category', 'memory' );
} }
$html = $mustache->render( 'laps', array( 'events' => $event_data ) ); $query_data = array();
$last_query_end = 0;
$last_offset = 0;
$last_duration = 0;
$category = 'query';
$wp_admin_bar->add_node( array( if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
foreach ( $wpdb->queries as $key => $query ) {
$query_start = isset( self::$query_starts[$key] ) ? self::$query_starts[$key] : $last_query_end;
list( $sql, $duration, $trace ) = $query;
$duration *= 1000;
$last_query_end = $query_start + $duration;
$offset = round( ( $query_start - $start ) / $total * 100, 2 );
// if query is indistinguishably close to previous then stack it
if ( $offset == $last_offset ) {
$key = count( $query_data ) - 1;
$query_data[$key]['sql'] .= '<br />' . $sql;
$last_duration += $duration;
$width = round( $last_duration / $total * 100, 2 );
$query_data[$key]['width'] = $width;
continue;
}
$width = round( $duration / $total * 100, 2 );
$last_offset = $offset;
$last_duration = $duration;
$query_data[] = compact( 'sql', 'duration', 'offset', 'width', 'category' );
}
}
// !d( self::$query_starts, $wpdb->queries, $query_data );
$html = $mustache->render(
'laps',
array(
'events' => $event_data,
'queries' => $query_data,
'savequeries' => defined( 'SAVEQUERIES' ) && SAVEQUERIES,
'http' => $http_data,
'savehttp' => ! empty( $http_data ),
)
);
$wp_admin_bar->add_node(
array(
'id' => 'laps', 'id' => 'laps',
'title' => sprintf( 'Lap: %ss', round( $total / 1000, 3 ) ), 'title' => sprintf( 'Lap: %ss', round( $total / 1000, 3 ) ),
) ); )
);
$wp_admin_bar->add_node( array( $wp_admin_bar->add_node(
array(
'id' => 'laps_output', 'id' => 'laps_output',
'parent' => 'laps', 'parent' => 'laps',
'meta' => array( 'html' => $html ), 'meta' => array( 'html' => $html ),
) ); )
);
} }
} }
+11
View File
@@ -2,13 +2,24 @@
namespace Rarst\Laps; namespace Rarst\Laps;
/**
* Events for Theme Hook Alliance
*
* @link http://zamoose.github.io/themehookalliance/
*/
class THA_Events extends Laps_Events { class THA_Events extends Laps_Events {
/**
* @return bool
*/
protected function is_applicable() { protected function is_applicable() {
return defined( 'THA_HOOKS_VERSION' ); return defined( 'THA_HOOKS_VERSION' );
} }
/**
* @return array
*/
protected function get_events() { protected function get_events() {
$events = array(); $events = array();
+11
View File
@@ -2,13 +2,24 @@
namespace Rarst\Laps; namespace Rarst\Laps;
/**
* Events for Thematic Framework based themes
*
* @link http://thematictheme.com/
*/
class Thematic_Events extends Laps_Events { class Thematic_Events extends Laps_Events {
/**
* @return bool
*/
protected function is_applicable() { protected function is_applicable() {
return function_exists( 'thematic_init' ); return function_exists( 'thematic_init' );
} }
/**
* @return array
*/
protected function get_events() { protected function get_events() {
$events = array(); $events = array();
+17 -1
View File
@@ -1,5 +1,21 @@
<div class="laps-timeline"> <div class="laps-timeline">
{{#events}} {{#events}}
<div class="event event-{{category}} hint--bottom" data-hint="{{name}} - {{duration}} ms - {{memory}} MB" style="width:{{width}}%; left:{{offset}}%;"></div> <div class="event event-{{category}}" data-toggle="tooltip" title="{{name}} - {{duration}} ms - {{memory}} MB" style="width:{{width}}%; left:{{offset}}%;"></div>
{{/events}} {{/events}}
</div> </div>
{{#savequeries}}
<div class="laps-timeline">
{{#queries}}
<div class="event event-{{category}}" title="{{sql}}" style="width:{{width}}%; left:{{offset}}%;"></div>
{{/queries}}
</div>
{{/savequeries}}
{{#savehttp}}
<div class="laps-timeline">
{{#http}}
<div class="event event-{{category}}" title="{{name}} - {{duration}} ms" style="width:{{width}}%; left:{{offset}}%;"></div>
{{/http}}
</div>
{{/savehttp}}