├── 02-features
├── closures
│ ├── arraymap.php
│ ├── attach-state-bindto.php
│ ├── attach-state-use.php
│ └── simple.php
├── generators
│ ├── csv-generator.php
│ ├── data.csv
│ ├── range-generator-BAD.php
│ ├── range-generator-GOOD.php
│ └── simple-generator.php
├── interfaces
│ ├── CommandOutputDocument.php
│ ├── DocumentStore.php
│ ├── Documentable.php
│ ├── HtmlDocument.php
│ ├── StreamDocument.php
│ ├── index.php
│ └── stream.txt
├── namespaces
│ ├── .gitignore
│ ├── 01-http-foundation.php
│ ├── 02-http-foundation.php
│ ├── 03-http-foundation.php
│ ├── 04-namespace-qualification.php
│ ├── 05-namespace-qualification.php
│ └── composer.json
└── traits
│ ├── .gitignore
│ ├── Geocodable.php
│ ├── RetailStore.php
│ ├── composer.json
│ └── index.php
├── 03-standards
├── psr3
│ ├── .gitignore
│ ├── composer.json
│ ├── index.php
│ └── logs
│ │ ├── development.log
│ │ └── production.log
└── psr4
│ ├── index.php
│ └── src
│ └── Bar.php
├── 04-components
├── url-scanner-app
│ ├── .gitignore
│ ├── composer.json
│ ├── scan.php
│ └── urls.csv
└── url-scanner-component
│ ├── .gitignore
│ ├── composer.json
│ ├── example.php
│ └── src
│ └── Url
│ └── Scanner.php
├── 05-good-practices
├── databases
│ ├── pdo-constructor-settings.php
│ ├── pdo-constructor.php
│ ├── prepared-statement-email.php
│ ├── prepared-statement-id.php
│ ├── prepared-statement-results.php
│ ├── schema.sql
│ ├── settings.php
│ ├── transactions-with.php
│ └── transactions-without.php
├── dates-and-times
│ ├── dateinterval-class-inverted.php
│ ├── dateinterval-class.php
│ ├── dateperiod-class-options.php
│ ├── dateperiod-class.php
│ ├── datetime-class.php
│ ├── datetimezone-class.php
│ └── set-default-timezone.php
├── exceptions
│ ├── error-handler.php
│ ├── exception-catch-multiple.php
│ ├── exception-catch.php
│ ├── exception-handler.php
│ ├── monolog
│ │ ├── .gitignore
│ │ ├── composer.json
│ │ ├── development.php
│ │ ├── logs
│ │ │ ├── development.log
│ │ │ └── production.log
│ │ └── production.php
│ └── whoops
│ │ ├── .gitignore
│ │ ├── composer.json
│ │ └── index.php
├── passwords
│ ├── login.php
│ └── register.php
├── sanitize-validate-escape
│ ├── escape-output.php
│ ├── sanitize-email.php
│ ├── sanitize-htmlentities.php
│ ├── sanitize-international.php
│ ├── sanitize-sql-bad.php
│ └── validate-email.php
└── streams
│ ├── DirtyWordsFilter.php
│ ├── context.php
│ ├── data.txt
│ ├── file-wrapper-explicit.php
│ ├── file-wrapper-implicit.php
│ ├── filter-upper-instantiation.php
│ ├── filter-upper.php
│ ├── flickr-api.php
│ └── log-iterator.php
├── 09-deployment
├── Capfile
├── composer.json
├── config
│ ├── deploy.rb
│ └── deploy
│ │ └── production.rb
└── public
│ └── index.php
├── 10-testing
├── .gitignore
├── .travis.yml
├── composer.json
├── coverage
│ ├── Whovian.php.html
│ ├── css
│ │ ├── bootstrap.min.css
│ │ ├── nv.d3.css
│ │ └── style.css
│ ├── dashboard.html
│ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.svg
│ │ ├── glyphicons-halflings-regular.ttf
│ │ └── glyphicons-halflings-regular.woff
│ ├── index.html
│ └── js
│ │ ├── bootstrap.min.js
│ │ ├── d3.min.js
│ │ ├── holder.js
│ │ ├── html5shiv.min.js
│ │ ├── jquery.min.js
│ │ ├── nv.d3.min.js
│ │ └── respond.min.js
├── phpunit.xml
├── src
│ └── Whovian.php
└── tests
│ ├── WhovianTest.php
│ └── bootstrap.php
├── LICENSE
└── README.md
/02-features/closures/arraymap.php:
--------------------------------------------------------------------------------
1 | [2,3,4]
7 |
--------------------------------------------------------------------------------
/02-features/closures/attach-state-bindto.php:
--------------------------------------------------------------------------------
1 | routes[$routePath] = $routeCallback->bindTo($this, __CLASS__);
12 | }
13 |
14 | public function dispatch($currentPath)
15 | {
16 | foreach ($this->routes as $routePath => $callback) {
17 | if ($routePath === $currentPath) {
18 | $callback();
19 | }
20 | }
21 |
22 | header('HTTP/1.1 ' . $this->responseStatus);
23 | header('Content-type: ' . $this->responseContentType);
24 | header('Content-length: ' . mb_strlen($this->responseBody));
25 | echo $this->responseBody;
26 | }
27 | }
28 |
29 | $app = new App();
30 | $app->addRoute('/users/josh', function () {
31 | $this->responseContentType = 'application/json;charset=utf8';
32 | $this->responseBody = '{"name": "Josh"}';
33 | });
34 | $app->dispatch('/users/josh');
35 |
--------------------------------------------------------------------------------
/02-features/closures/attach-state-use.php:
--------------------------------------------------------------------------------
1 | "Clay, get me sweet tea!"
14 |
--------------------------------------------------------------------------------
/02-features/closures/simple.php:
--------------------------------------------------------------------------------
1 | "Hello Josh"
8 |
--------------------------------------------------------------------------------
/02-features/generators/csv-generator.php:
--------------------------------------------------------------------------------
1 | command = $command;
9 | }
10 |
11 | public function getId()
12 | {
13 | return $this->command;
14 | }
15 |
16 | public function getContent()
17 | {
18 | return shell_exec($this->command);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/02-features/interfaces/DocumentStore.php:
--------------------------------------------------------------------------------
1 | getId();
9 | $value = $document->getContent();
10 | $this->data[$key] = $value;
11 | }
12 |
13 | public function getDocuments()
14 | {
15 | return $this->data;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/02-features/interfaces/Documentable.php:
--------------------------------------------------------------------------------
1 | url = $url;
9 | }
10 |
11 | public function getId()
12 | {
13 | return $this->url;
14 | }
15 |
16 | public function getContent()
17 | {
18 | $ch = curl_init();
19 | curl_setopt($ch, CURLOPT_URL, $this->url);
20 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
21 | curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);
22 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
23 | curl_setopt($ch, CURLOPT_MAXREDIRS, 3);
24 | $html = curl_exec($ch);
25 | curl_close($ch);
26 |
27 | return $html;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/02-features/interfaces/StreamDocument.php:
--------------------------------------------------------------------------------
1 | resource = $resource;
10 | $this->buffer = $buffer;
11 | }
12 |
13 | public function getId()
14 | {
15 | return 'resource-' . (int)$this->resource;
16 | }
17 |
18 | public function getContent()
19 | {
20 | $streamContent = '';
21 | rewind($this->resource);
22 | while (feof($this->resource) === false) {
23 | $streamContent .= fread($this->resource, $this->buffer);
24 | }
25 |
26 | return $streamContent;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/02-features/interfaces/index.php:
--------------------------------------------------------------------------------
1 | addDocument($htmlDoc);
13 |
14 | // Add stream document
15 | $streamDoc = new StreamDocument(fopen('stream.txt', 'rb'));
16 | $documentStore->addDocument($streamDoc);
17 |
18 | // Add terminal command document
19 | $cmdDoc = new CommandOutputDocument('cat /etc/hosts');
20 | $documentStore->addDocument($cmdDoc);
21 |
22 | print_r($documentStore->getDocuments());
23 |
--------------------------------------------------------------------------------
/02-features/interfaces/stream.txt:
--------------------------------------------------------------------------------
1 | This is stream content.
2 |
--------------------------------------------------------------------------------
/02-features/namespaces/.gitignore:
--------------------------------------------------------------------------------
1 | vendor/*
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/02-features/namespaces/01-http-foundation.php:
--------------------------------------------------------------------------------
1 | send();
5 |
--------------------------------------------------------------------------------
/02-features/namespaces/02-http-foundation.php:
--------------------------------------------------------------------------------
1 | send();
8 |
--------------------------------------------------------------------------------
/02-features/namespaces/03-http-foundation.php:
--------------------------------------------------------------------------------
1 | send();
8 |
--------------------------------------------------------------------------------
/02-features/namespaces/04-namespace-qualification.php:
--------------------------------------------------------------------------------
1 | geocoder = $geocoder;
16 | }
17 |
18 | public function setAddress($address)
19 | {
20 | $this->address = $address;
21 | }
22 |
23 | public function getLatitude()
24 | {
25 | if (!isset($this->geocoderResult)) {
26 | $this->geocodeAddress();
27 | }
28 |
29 | return $this->geocoderResult->first()->getLatitude();
30 | }
31 |
32 | public function getLongitude()
33 | {
34 | if (!isset($this->geocoderResult)) {
35 | $this->geocodeAddress();
36 | }
37 |
38 | return $this->geocoderResult->first()->getLongitude();
39 | }
40 |
41 | protected function geocodeAddress()
42 | {
43 | $this->geocoderResult = $this->geocoder->geocode($this->address);
44 |
45 | return true;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/02-features/traits/RetailStore.php:
--------------------------------------------------------------------------------
1 | setAddress('420 9th Avenue, New York, NY 10001 USA');
11 | $store->setGeocoder($geocoder);
12 |
13 | $latitude = $store->getLatitude();
14 | $longitude = $store->getLongitude();
15 |
16 | echo $latitude, ':', $longitude;
17 |
--------------------------------------------------------------------------------
/03-standards/psr3/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/03-standards/psr3/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "monolog/monolog": "^1.17"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/03-standards/psr3/index.php:
--------------------------------------------------------------------------------
1 | pushHandler(new StreamHandler('logs/development.log', Logger::DEBUG));
12 | $log->pushHandler(new StreamHandler('logs/production.log', Logger::WARNING));
13 |
14 | // Use logger
15 | $log->debug('This is a debug message');
16 | $log->warning('This is a warning message');
17 |
--------------------------------------------------------------------------------
/03-standards/psr3/logs/development.log:
--------------------------------------------------------------------------------
1 | [2016-01-23 11:16:47] myApp.DEBUG: This is a debug message [] []
2 | [2016-01-23 11:16:47] myApp.WARNING: This is a warning message [] []
3 |
--------------------------------------------------------------------------------
/03-standards/psr3/logs/production.log:
--------------------------------------------------------------------------------
1 | [2016-01-23 11:16:47] myApp.WARNING: This is a warning message [] []
2 |
--------------------------------------------------------------------------------
/03-standards/psr4/index.php:
--------------------------------------------------------------------------------
1 | sayHello();
45 |
--------------------------------------------------------------------------------
/03-standards/psr4/src/Bar.php:
--------------------------------------------------------------------------------
1 | options($csvRow[0]);
14 |
15 | // 5. Inspect HTTP response status code
16 | if ($httpResponse->getStatusCode() >= 400) {
17 | throw new \Exception();
18 | }
19 | } catch (\Exception $e) {
20 | // 6. Send bad URLs to standard out
21 | echo $csvRow[0] . PHP_EOL;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/04-components/url-scanner-app/urls.csv:
--------------------------------------------------------------------------------
1 | https://apple.com
2 | http://sdsdg.org
3 | http://php.net
4 |
--------------------------------------------------------------------------------
/04-components/url-scanner-component/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/04-components/url-scanner-component/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "modernphp/scanner",
3 | "description": "Scan URLs from a CSV file and report inaccessible URLs",
4 | "keywords": ["url", "scanner", "csv"],
5 | "homepage": "http://example.com",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Josh Lockhart",
10 | "homepage": "https://github.com/codeguy",
11 | "role": "Developer"
12 | }
13 | ],
14 | "support": {
15 | "email": "help@example.com"
16 | },
17 | "require": {
18 | "php" : ">=5.6.0",
19 | "guzzlehttp/guzzle": "^6.1"
20 | },
21 | "require-dev": {
22 | "phpunit/phpunit": "^5.0"
23 | },
24 | "suggest": {
25 | "league/csv": "^8.0"
26 | },
27 | "autoload": {
28 | "psr-4": {
29 | "Oreilly\\ModernPHP\\": "src/"
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/04-components/url-scanner-component/example.php:
--------------------------------------------------------------------------------
1 | getInvalidUrls());
11 |
--------------------------------------------------------------------------------
/04-components/url-scanner-component/src/Url/Scanner.php:
--------------------------------------------------------------------------------
1 | urls = $urls;
23 | $this->httpClient = new \GuzzleHttp\Client();
24 | }
25 |
26 | /**
27 | * Get invalid URLs
28 | * @return array
29 | */
30 | public function getInvalidUrls()
31 | {
32 | $invalidUrls = [];
33 | foreach ($this->urls as $url) {
34 | try {
35 | $statusCode = $this->getStatusCodeForUrl($url);
36 | } catch (\Exception $e) {
37 | $statusCode = 500;
38 | }
39 |
40 | if ($statusCode >= 400) {
41 | array_push($invalidUrls, [
42 | 'url' => $url,
43 | 'status' => $statusCode
44 | ]);
45 | }
46 | }
47 |
48 | return $invalidUrls;
49 | }
50 |
51 | /**
52 | * Get HTTP status code for URL
53 | * @param string $url The remote URL
54 | * @return int The HTTP status code
55 | */
56 | protected function getStatusCodeForUrl($url)
57 | {
58 | $httpResponse = $this->httpClient->options($url);
59 |
60 | return $httpResponse->getStatusCode();
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/05-good-practices/databases/pdo-constructor-settings.php:
--------------------------------------------------------------------------------
1 | prepare($sql);
28 | $statement->bindValue(':email', $email);
29 |
--------------------------------------------------------------------------------
/05-good-practices/databases/prepared-statement-id.php:
--------------------------------------------------------------------------------
1 | prepare($sql);
28 | $statement->bindValue(':id', $userId, PDO::PARAM_INT);
29 |
--------------------------------------------------------------------------------
/05-good-practices/databases/prepared-statement-results.php:
--------------------------------------------------------------------------------
1 | prepare($sql);
26 | $email = filter_input(INPUT_GET, 'email');
27 | $statement->bindValue(':email', $email, PDO::PARAM_INT);
28 |
29 | // Iterate results one at a time
30 | echo 'One result as a time as associative array', PHP_EOL;
31 | $statement->execute();
32 | while (($result = $statement->fetch(PDO::FETCH_ASSOC)) !== false) {
33 | echo $result['email'], PHP_EOL;
34 | }
35 |
36 | // Iterate ALL results at once
37 | echo 'All results at once as associative array', PHP_EOL;
38 | $statement->execute();
39 | $allResults = $statement->fetchAll(PDO::FETCH_ASSOC);
40 | foreach ($allResults as $result) {
41 | echo $result['email'], PHP_EOL;
42 | }
43 |
44 | // Fetch one column value at a time
45 | echo 'Fetch one column, one row at a time as associative array', PHP_EOL;
46 | $statement->execute();
47 | while (($email = $statement->fetchColumn(1)) !== false) {
48 | echo $email, PHP_EOL;
49 | }
50 |
51 | // Iterate results as objects
52 | echo 'One result as a time as object', PHP_EOL;
53 | $statement->execute();
54 | while (($result = $statement->fetchObject()) !== false) {
55 | echo $result->email, PHP_EOL;
56 | }
57 |
--------------------------------------------------------------------------------
/05-good-practices/databases/schema.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS books;
2 | CREATE TABLE books (
3 | id INT UNSIGNED NOT NULL AUTO_INCREMENT,
4 | title TEXT,
5 | author TEXT,
6 | isbn TEXT,
7 | PRIMARY KEY (id)
8 | );
9 |
10 | INSERT INTO books VALUES (NULL, 'Sahara', 'Clive Cussler', '9781439135686');
11 | INSERT INTO books VALUES (NULL, 'Atlantis Found', 'Clive Cussler', '9780425177174');
12 | INSERT INTO books VALUES (NULL, 'Inca Gold', 'Clive Cussler', '9781416525721');
13 |
14 | DROP TABLE IF EXISTS users;
15 | CREATE TABLE users (
16 | id INT UNSIGNED NOT NULL AUTO_INCREMENT,
17 | email VARCHAR(255) NOT NULL,
18 | password VARCHAR(255) NOT NULL,
19 |
20 | PRIMARY KEY (id)
21 | );
22 |
23 | # Real password is "password"
24 | INSERT INTO users VALUES (NULL, 'john@example.com', '$2y$10$q/39UmDLlVZvk9z.bHhgVuqKyVIYq4OibWs697.52qCdJvufJayg2');
25 |
26 | DROP TABLE IF EXISTS accounts;
27 | CREATE TABLE accounts (
28 | id INT UNSIGNED NOT NULL AUTO_INCREMENT,
29 | name VARCHAR(255),
30 | amount DECIMAL(10,2),
31 | PRIMARY KEY (id)
32 | );
33 |
34 | INSERT INTO accounts VALUES (NULL, 'Checking', 1500.50);
35 | INSERT INTO accounts VALUES (NULL, 'Savings', 75500.20);
36 |
--------------------------------------------------------------------------------
/05-good-practices/databases/settings.php:
--------------------------------------------------------------------------------
1 | '127.0.0.1',
4 | 'port' => '3306',
5 | 'name' => 'books',
6 | 'username' => 'root',
7 | 'password' => 'jlwaxhaw',
8 | 'charset' => 'utf8'
9 | ];
10 |
--------------------------------------------------------------------------------
/05-good-practices/databases/transactions-with.php:
--------------------------------------------------------------------------------
1 | prepare('
25 | UPDATE accounts
26 | SET amount = amount - :amount
27 | WHERE name = :name
28 | ');
29 | $stmtAdd = $pdo->prepare('
30 | UPDATE accounts
31 | SET amount = amount + :amount
32 | WHERE name = :name
33 | ');
34 |
35 | // Start transaction
36 | $pdo->beginTransaction();
37 |
38 | // Withdraw funds from account 1
39 | $fromAccount = 'Checking';
40 | $withdrawal = 50;
41 | $stmtSubtract->bindParam(':name', $fromAccount);
42 | $stmtSubtract->bindParam(':amount', $withDrawal, PDO::PARAM_INT);
43 | $stmtSubtract->execute();
44 |
45 | // Deposit funds into account 2
46 | $toAccount = 'Savings';
47 | $deposit = 50;
48 | $stmtAdd->bindParam(':name', $toAccount);
49 | $stmtAdd->bindParam(':amount', $deposit, PDO::PARAM_INT);
50 | $stmtAdd->execute();
51 |
52 | // Commit transaction
53 | $pdo->commit();
54 |
--------------------------------------------------------------------------------
/05-good-practices/databases/transactions-without.php:
--------------------------------------------------------------------------------
1 | prepare('
25 | UPDATE accounts
26 | SET amount = amount - :amount
27 | WHERE name = :name
28 | ');
29 | $stmtAdd = $pdo->prepare('
30 | UPDATE accounts
31 | SET amount = amount + :amount
32 | WHERE name = :name
33 | ');
34 |
35 | // Withdraw funds from account 1
36 | $fromAccount = 'Checking';
37 | $withdrawal = 50;
38 | $stmtSubtract->bindParam(':name', $fromAccount);
39 | $stmtSubtract->bindParam(':amount', $withDrawal, PDO::PARAM_INT);
40 | $stmtSubtract->execute();
41 |
42 | // Deposit funds into account 2
43 | $toAccount = 'Savings';
44 | $deposit = 50;
45 | $stmtAdd->bindParam(':name', $toAccount);
46 | $stmtAdd->bindParam(':amount', $deposit, PDO::PARAM_INT);
47 | $stmtAdd->execute();
48 |
--------------------------------------------------------------------------------
/05-good-practices/dates-and-times/dateinterval-class-inverted.php:
--------------------------------------------------------------------------------
1 | format('Y-m-d'), PHP_EOL;
9 | }
10 |
--------------------------------------------------------------------------------
/05-good-practices/dates-and-times/dateinterval-class.php:
--------------------------------------------------------------------------------
1 | add($interval);
12 | echo $datetime->format('Y-m-d H:i:s');
13 |
--------------------------------------------------------------------------------
/05-good-practices/dates-and-times/dateperiod-class-options.php:
--------------------------------------------------------------------------------
1 | format('Y-m-d H:i:s'), PHP_EOL;
15 | }
16 |
--------------------------------------------------------------------------------
/05-good-practices/dates-and-times/dateperiod-class.php:
--------------------------------------------------------------------------------
1 | format('Y-m-d H:i:s'), PHP_EOL;
10 | }
11 |
--------------------------------------------------------------------------------
/05-good-practices/dates-and-times/datetime-class.php:
--------------------------------------------------------------------------------
1 | setTimezone(new DateTimeZone('Asia/Hong_Kong'));
7 |
--------------------------------------------------------------------------------
/05-good-practices/dates-and-times/set-default-timezone.php:
--------------------------------------------------------------------------------
1 | getCode();
7 | $message = $e->getMessage();
8 |
9 | // Display a nice message to the user
10 | echo 'Something went wrong. Check back soon, please.';
11 | exit;
12 | }
13 |
--------------------------------------------------------------------------------
/05-good-practices/exceptions/exception-handler.php:
--------------------------------------------------------------------------------
1 | getMessage();
6 | });
7 |
8 | // Your code goes here...
9 | throw new \Exception("Someting went wrong!");
10 |
11 | // Restore previous exception handler
12 | restore_exception_handler();
13 |
--------------------------------------------------------------------------------
/05-good-practices/exceptions/monolog/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/05-good-practices/exceptions/monolog/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "monolog/monolog": "~1.11",
4 | "swiftmailer/swiftmailer": "~5.3"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/05-good-practices/exceptions/monolog/development.php:
--------------------------------------------------------------------------------
1 | pushHandler(new StreamHandler('logs/development.log', Logger::WARNING));
12 |
13 | // Use logger
14 | $log->warning('This is a warning!');
15 |
--------------------------------------------------------------------------------
/05-good-practices/exceptions/monolog/logs/development.log:
--------------------------------------------------------------------------------
1 | [2014-12-13 19:39:00] my-app-name.WARNING: This is a warning! [] []
2 |
--------------------------------------------------------------------------------
/05-good-practices/exceptions/monolog/logs/production.log:
--------------------------------------------------------------------------------
1 | [2014-12-13 19:41:26] my-app-name.CRITICAL: The server is on fire! [] []
2 | [2014-12-13 14:41:46] my-app-name.CRITICAL: The server is on fire! [] []
3 |
--------------------------------------------------------------------------------
/05-good-practices/exceptions/monolog/production.php:
--------------------------------------------------------------------------------
1 | pushHandler(new StreamHandler('logs/production.log', Logger::WARNING));
15 |
16 | // Add SwiftMailer handler for critical errors
17 | $transport = \Swift_SmtpTransport::newInstance('smtp.example.com', 587)
18 | ->setUsername('USERNAME')
19 | ->setPassword('PASSWORD');
20 | $mailer = \Swift_Mailer::newInstance($transport);
21 | $message = \Swift_Message::newInstance()
22 | ->setSubject('Website error!')
23 | ->setFrom(array('daemon@example.com' => 'John Doe'))
24 | ->setTo(array('admin@example.com'));
25 | $log->pushHandler(new SwiftMailerHandler($mailer, $message, Logger::CRITICAL));
26 |
27 | // Use logger
28 | $log->critical('The server is on fire!');
29 |
--------------------------------------------------------------------------------
/05-good-practices/exceptions/whoops/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/05-good-practices/exceptions/whoops/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "filp/whoops": "~1.0"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/05-good-practices/exceptions/whoops/index.php:
--------------------------------------------------------------------------------
1 | pushHandler(new \Whoops\Handler\PrettyPageHandler);
8 | $whoops->register();
9 |
10 | throw new \Exception('This is an exception!');
11 |
--------------------------------------------------------------------------------
/05-good-practices/passwords/login.php:
--------------------------------------------------------------------------------
1 | password_hash) === false) {
15 | throw new Exception('Invalid password');
16 | }
17 |
18 | // Re-hash password if necessary (see note below)
19 | $currentHashAlgorithm = PASSWORD_DEFAULT;
20 | $currentHashOptions = array('cost' => 15);
21 | $passwordNeedsRehash = password_needs_rehash(
22 | $user->password_hash,
23 | $currentHashAlgorithm,
24 | $currentHashOptions
25 | );
26 | if ($passwordNeedsRehash === true) {
27 | // Save new password hash (THIS IS PSUEDO-CODE)
28 | $user->password_hash = password_hash(
29 | $password,
30 | $currentHashAlgorithm,
31 | $currentHashOptions
32 | );
33 | $user->save();
34 | }
35 |
36 | // Save login status to session
37 | $_SESSION['user_logged_in'] = 'yes';
38 | $_SESSION['user_email'] = $email;
39 |
40 | // Redirect to profile page
41 | header('HTTP/1.1 302 Redirect');
42 | header('Location: /user-profile.php');
43 | } catch (Exception $e) {
44 | header('HTTP/1.1 401 Unauthorized');
45 | echo $e->getMessage();
46 | }
47 |
--------------------------------------------------------------------------------
/05-good-practices/passwords/register.php:
--------------------------------------------------------------------------------
1 | 12]
20 | );
21 | if ($passwordHash === false) {
22 | throw new Exception('Password hash failed');
23 | }
24 |
25 | // Create user account (THIS IS PSUEDO-CODE)
26 | // $user = new User();
27 | // $user->email = $email;
28 | // $user->password_hash = $passwordHash;
29 | // $user->save();
30 |
31 | // Redirect to login page
32 | header('HTTP/1.1 302 Redirect');
33 | header('Location: /login.php');
34 | } catch (Exception $e) {
35 | // Report error
36 | header('HTTP/1.1 400 Bad request');
37 | echo $e->getMessage();
38 | }
39 |
--------------------------------------------------------------------------------
/05-good-practices/sanitize-validate-escape/escape-output.php:
--------------------------------------------------------------------------------
1 | ';
3 | echo htmlentities($output, ENT_QUOTES, 'UTF-8');
4 |
--------------------------------------------------------------------------------
/05-good-practices/sanitize-validate-escape/sanitize-email.php:
--------------------------------------------------------------------------------
1 |
';
3 | echo htmlentities($input, ENT_QUOTES, 'UTF-8');
4 |
--------------------------------------------------------------------------------
/05-good-practices/sanitize-validate-escape/sanitize-international.php:
--------------------------------------------------------------------------------
1 | data = str_replace($bad, $good, $bucket->data);
26 |
27 | // Increment total data consumed
28 | $consumed += $bucket->datalen;
29 |
30 | // Send bucket to downstream brigade
31 | stream_bucket_append($out, $bucket);
32 | }
33 |
34 | return PSFS_PASS_ON;
35 | }
36 | }
37 | stream_filter_register('dirty_words_filter', 'DirtyWordsFilter');
38 |
39 | // Use custom filter
40 | $handle = fopen('data.txt', 'rb');
41 | stream_filter_append($handle, 'dirty_words_filter');
42 | while (feof($handle) !== true) {
43 | echo fgets($handle);
44 | }
45 | fclose($handle);
46 |
--------------------------------------------------------------------------------
/05-good-practices/streams/context.php:
--------------------------------------------------------------------------------
1 | array(
5 | 'method' => 'POST',
6 | 'header' => "Content-Type: application/json;charset=utf-8;\r\n" .
7 | "Content-Length: " . mb_strlen($requestBody),
8 | 'content' => $requestBody
9 | )
10 | ));
11 | $response = file_get_contents('https://my-api.com/users', false, $context);
12 |
--------------------------------------------------------------------------------
/05-good-practices/streams/data.txt:
--------------------------------------------------------------------------------
1 | Lorem ipsum dolor sit amet, dirt conseCtetur adipisicing elit. Totam blanditiis fugiat quia error, quI maiores grime laudantium eveniet unde quidem animi alias ducimus facilis grease nobis ratione ipsa, natus placeat distinctio deserunt?
2 |
--------------------------------------------------------------------------------
/05-good-practices/streams/file-wrapper-explicit.php:
--------------------------------------------------------------------------------
1 | format('Y-m-d') . '.log.bz2';
7 | if (file_exists($file)) {
8 | $handle = fopen($file, 'rb');
9 | stream_filter_append($handle, 'bzip2.decompress');
10 | while (feof($handle) !== true) {
11 | $line = fgets($handle);
12 | if (strpos($line, 'www.example.com') !== false) {
13 | fwrite(STDOUT, $line);
14 | }
15 | }
16 | fclose($handle);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/09-deployment/Capfile:
--------------------------------------------------------------------------------
1 | # Load DSL and set up stages
2 | require 'capistrano/setup'
3 |
4 | # Include default deployment tasks
5 | require 'capistrano/deploy'
6 |
7 | # Include tasks from other gems included in your Gemfile
8 | #
9 | # For documentation on these, see for example:
10 | #
11 | # https://github.com/capistrano/rvm
12 | # https://github.com/capistrano/rbenv
13 | # https://github.com/capistrano/chruby
14 | # https://github.com/capistrano/bundler
15 | # https://github.com/capistrano/rails
16 | # https://github.com/capistrano/passenger
17 | #
18 | # require 'capistrano/rvm'
19 | # require 'capistrano/rbenv'
20 | # require 'capistrano/chruby'
21 | # require 'capistrano/bundler'
22 | # require 'capistrano/rails/assets'
23 | # require 'capistrano/rails/migrations'
24 | # require 'capistrano/passenger'
25 |
26 | # Load custom tasks from `lib/capistrano/tasks' if you have any defined
27 | Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
28 |
--------------------------------------------------------------------------------
/09-deployment/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require": {
3 | "symfony/http-foundation": "~2.6"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/09-deployment/config/deploy.rb:
--------------------------------------------------------------------------------
1 | # config valid only for current version of Capistrano
2 | lock '3.3.5'
3 |
4 | set :application, 'APP'
5 | set :repo_url, 'git@github.com:USERNAME/REPO.git'
6 |
7 | # Default branch is :master
8 | # ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call
9 |
10 | # Default deploy_to directory is /var/www/my_app_name
11 | set :deploy_to, '/home/deploy/apps/my_app'
12 |
13 | # Default value for :scm is :git
14 | # set :scm, :git
15 |
16 | # Default value for :format is :pretty
17 | # set :format, :pretty
18 |
19 | # Default value for :log_level is :debug
20 | # set :log_level, :debug
21 |
22 | # Default value for :pty is false
23 | # set :pty, true
24 |
25 | # Default value for :linked_files is []
26 | # set :linked_files, fetch(:linked_files, []).push('config/database.yml')
27 |
28 | # Default value for linked_dirs is []
29 | # set :linked_dirs, fetch(:linked_dirs, []).push('bin', 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
30 |
31 | # Default value for default_env is {}
32 | # set :default_env, { path: "/opt/ruby/bin:$PATH" }
33 |
34 | # Default value for keep_releases is 5
35 | set :keep_releases, 5
36 |
37 | namespace :deploy do
38 | desc "Build"
39 | after :updated, :build do
40 | on roles(:web) do
41 | within release_path do
42 | execute :composer, "install --no-dev --quiet"
43 | end
44 | end
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/09-deployment/config/deploy/production.rb:
--------------------------------------------------------------------------------
1 | role :web, %w{deploy@123.456.78.90}
2 |
--------------------------------------------------------------------------------
/09-deployment/public/index.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Code Coverage for /Users/josh/Repos/modern-php/repo/10-testing/src/Whovian.php
6 |
7 |
8 |
9 |
13 |
14 |
15 |
28 |
29 |
30 |
31 |
32 | |
33 | Code Coverage |
34 |
35 |
36 | |
37 | Classes and Traits |
38 | Functions and Methods |
39 | Lines |
40 |
41 |
42 |
43 |
44 | Total |
45 |
46 |
47 | 100.00% covered (success)
48 |
49 |
50 | |
51 | 100.00% |
52 | 1 / 1 |
53 |
54 |
55 | 100.00% covered (success)
56 |
57 |
58 | |
59 | 100.00% |
60 | 3 / 3 |
61 | CRAP |
62 |
63 |
64 | 100.00% covered (success)
65 |
66 |
67 | |
68 | 100.00% |
69 | 13 / 13 |
70 |
71 |
72 |
73 | Whovian |
74 |
75 |
76 | 100.00% covered (success)
77 |
78 |
79 | |
80 | 100.00% |
81 | 1 / 1 |
82 |
83 |
84 | 100.00% covered (success)
85 |
86 |
87 | |
88 | 100.00% |
89 | 3 / 3 |
90 | 4 |
91 |
92 |
93 | 100.00% covered (success)
94 |
95 |
96 | |
97 | 100.00% |
98 | 13 / 13 |
99 |
100 |
101 |
102 | __construct($favoriteDoctor) |
103 |
104 |
105 | 100.00% covered (success)
106 |
107 |
108 | |
109 | 100.00% |
110 | 1 / 1 |
111 | 1 |
112 |
113 |
114 | 100.00% covered (success)
115 |
116 |
117 | |
118 | 100.00% |
119 | 2 / 2 |
120 |
121 |
122 |
123 | say() |
124 |
125 |
126 | 100.00% covered (success)
127 |
128 |
129 | |
130 | 100.00% |
131 | 1 / 1 |
132 | 1 |
133 |
134 |
135 | 100.00% covered (success)
136 |
137 |
138 | |
139 | 100.00% |
140 | 1 / 1 |
141 |
142 |
143 |
144 | respondTo($input) |
145 |
146 |
147 | 100.00% covered (success)
148 |
149 |
150 | |
151 | 100.00% |
152 | 1 / 1 |
153 | 2 |
154 |
155 |
156 | 100.00% covered (success)
157 |
158 |
159 | |
160 | 100.00% |
161 | 10 / 10 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 | | <?php |
170 | | class Whovian |
171 | | { |
172 | | |
173 | | |
174 | | |
175 | | protected $favoriteDoctor; |
176 | | |
177 | | |
178 | | |
179 | | |
180 | | |
181 | | public function __construct($favoriteDoctor) |
182 | | { |
183 | | $this->favoriteDoctor = (string)$favoriteDoctor; |
184 | | } |
185 | | |
186 | | |
187 | | |
188 | | |
189 | | |
190 | | public function say() |
191 | | { |
192 | | return 'The best doctor is ' . $this->favoriteDoctor; |
193 | | } |
194 | | |
195 | | |
196 | | |
197 | | |
198 | | |
199 | | |
200 | | |
201 | | public function respondTo($input) |
202 | | { |
203 | | $input = strtolower($input); |
204 | | $myDoctor = strtolower($this->favoriteDoctor); |
205 | | |
206 | | if (strpos($input, $myDoctor) === false) { |
207 | | throw new Exception( |
208 | | sprintf( |
209 | | 'No way! %s is the best doctor ever!', |
210 | | $this->favoriteDoctor |
211 | | ) |
212 | | ); |
213 | | } |
214 | | |
215 | | return 'I agree!'; |
216 | | } |
217 | | } |
218 |
219 |
220 |
221 |
234 |
235 |
236 |
237 |
238 |
261 |
262 |
263 |
--------------------------------------------------------------------------------
/10-testing/coverage/css/nv.d3.css:
--------------------------------------------------------------------------------
1 |
2 | /********************
3 | * HTML CSS
4 | */
5 |
6 |
7 | .chartWrap {
8 | margin: 0;
9 | padding: 0;
10 | overflow: hidden;
11 | }
12 |
13 | /********************
14 | Box shadow and border radius styling
15 | */
16 | .nvtooltip.with-3d-shadow, .with-3d-shadow .nvtooltip {
17 | -moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
18 | -webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
19 | box-shadow: 0 5px 10px rgba(0,0,0,.2);
20 |
21 | -webkit-border-radius: 6px;
22 | -moz-border-radius: 6px;
23 | border-radius: 6px;
24 | }
25 |
26 | /********************
27 | * TOOLTIP CSS
28 | */
29 |
30 | .nvtooltip {
31 | position: absolute;
32 | background-color: rgba(255,255,255,1.0);
33 | padding: 1px;
34 | border: 1px solid rgba(0,0,0,.2);
35 | z-index: 10000;
36 |
37 | font-family: Arial;
38 | font-size: 13px;
39 | text-align: left;
40 | pointer-events: none;
41 |
42 | white-space: nowrap;
43 |
44 | -webkit-touch-callout: none;
45 | -webkit-user-select: none;
46 | -khtml-user-select: none;
47 | -moz-user-select: none;
48 | -ms-user-select: none;
49 | user-select: none;
50 | }
51 |
52 | /*Give tooltips that old fade in transition by
53 | putting a "with-transitions" class on the container div.
54 | */
55 | .nvtooltip.with-transitions, .with-transitions .nvtooltip {
56 | transition: opacity 250ms linear;
57 | -moz-transition: opacity 250ms linear;
58 | -webkit-transition: opacity 250ms linear;
59 |
60 | transition-delay: 250ms;
61 | -moz-transition-delay: 250ms;
62 | -webkit-transition-delay: 250ms;
63 | }
64 |
65 | .nvtooltip.x-nvtooltip,
66 | .nvtooltip.y-nvtooltip {
67 | padding: 8px;
68 | }
69 |
70 | .nvtooltip h3 {
71 | margin: 0;
72 | padding: 4px 14px;
73 | line-height: 18px;
74 | font-weight: normal;
75 | background-color: rgba(247,247,247,0.75);
76 | text-align: center;
77 |
78 | border-bottom: 1px solid #ebebeb;
79 |
80 | -webkit-border-radius: 5px 5px 0 0;
81 | -moz-border-radius: 5px 5px 0 0;
82 | border-radius: 5px 5px 0 0;
83 | }
84 |
85 | .nvtooltip p {
86 | margin: 0;
87 | padding: 5px 14px;
88 | text-align: center;
89 | }
90 |
91 | .nvtooltip span {
92 | display: inline-block;
93 | margin: 2px 0;
94 | }
95 |
96 | .nvtooltip table {
97 | margin: 6px;
98 | border-spacing:0;
99 | }
100 |
101 |
102 | .nvtooltip table td {
103 | padding: 2px 9px 2px 0;
104 | vertical-align: middle;
105 | }
106 |
107 | .nvtooltip table td.key {
108 | font-weight:normal;
109 | }
110 | .nvtooltip table td.value {
111 | text-align: right;
112 | font-weight: bold;
113 | }
114 |
115 | .nvtooltip table tr.highlight td {
116 | padding: 1px 9px 1px 0;
117 | border-bottom-style: solid;
118 | border-bottom-width: 1px;
119 | border-top-style: solid;
120 | border-top-width: 1px;
121 | }
122 |
123 | .nvtooltip table td.legend-color-guide div {
124 | width: 8px;
125 | height: 8px;
126 | vertical-align: middle;
127 | }
128 |
129 | .nvtooltip .footer {
130 | padding: 3px;
131 | text-align: center;
132 | }
133 |
134 |
135 | .nvtooltip-pending-removal {
136 | position: absolute;
137 | pointer-events: none;
138 | }
139 |
140 |
141 | /********************
142 | * SVG CSS
143 | */
144 |
145 |
146 | svg {
147 | -webkit-touch-callout: none;
148 | -webkit-user-select: none;
149 | -khtml-user-select: none;
150 | -moz-user-select: none;
151 | -ms-user-select: none;
152 | user-select: none;
153 | /* Trying to get SVG to act like a greedy block in all browsers */
154 | display: block;
155 | width:100%;
156 | height:100%;
157 | }
158 |
159 |
160 | svg text {
161 | font: normal 12px Arial;
162 | }
163 |
164 | svg .title {
165 | font: bold 14px Arial;
166 | }
167 |
168 | .nvd3 .nv-background {
169 | fill: white;
170 | fill-opacity: 0;
171 | /*
172 | pointer-events: none;
173 | */
174 | }
175 |
176 | .nvd3.nv-noData {
177 | font-size: 18px;
178 | font-weight: bold;
179 | }
180 |
181 |
182 | /**********
183 | * Brush
184 | */
185 |
186 | .nv-brush .extent {
187 | fill-opacity: .125;
188 | shape-rendering: crispEdges;
189 | }
190 |
191 |
192 |
193 | /**********
194 | * Legend
195 | */
196 |
197 | .nvd3 .nv-legend .nv-series {
198 | cursor: pointer;
199 | }
200 |
201 | .nvd3 .nv-legend .disabled circle {
202 | fill-opacity: 0;
203 | }
204 |
205 |
206 |
207 | /**********
208 | * Axes
209 | */
210 | .nvd3 .nv-axis {
211 | pointer-events:none;
212 | }
213 |
214 | .nvd3 .nv-axis path {
215 | fill: none;
216 | stroke: #000;
217 | stroke-opacity: .75;
218 | shape-rendering: crispEdges;
219 | }
220 |
221 | .nvd3 .nv-axis path.domain {
222 | stroke-opacity: .75;
223 | }
224 |
225 | .nvd3 .nv-axis.nv-x path.domain {
226 | stroke-opacity: 0;
227 | }
228 |
229 | .nvd3 .nv-axis line {
230 | fill: none;
231 | stroke: #e5e5e5;
232 | shape-rendering: crispEdges;
233 | }
234 |
235 | .nvd3 .nv-axis .zero line,
236 | /*this selector may not be necessary*/ .nvd3 .nv-axis line.zero {
237 | stroke-opacity: .75;
238 | }
239 |
240 | .nvd3 .nv-axis .nv-axisMaxMin text {
241 | font-weight: bold;
242 | }
243 |
244 | .nvd3 .x .nv-axis .nv-axisMaxMin text,
245 | .nvd3 .x2 .nv-axis .nv-axisMaxMin text,
246 | .nvd3 .x3 .nv-axis .nv-axisMaxMin text {
247 | text-anchor: middle
248 | }
249 |
250 |
251 |
252 | /**********
253 | * Brush
254 | */
255 |
256 | .nv-brush .resize path {
257 | fill: #eee;
258 | stroke: #666;
259 | }
260 |
261 |
262 |
263 | /**********
264 | * Bars
265 | */
266 |
267 | .nvd3 .nv-bars .negative rect {
268 | zfill: brown;
269 | }
270 |
271 | .nvd3 .nv-bars rect {
272 | zfill: steelblue;
273 | fill-opacity: .75;
274 |
275 | transition: fill-opacity 250ms linear;
276 | -moz-transition: fill-opacity 250ms linear;
277 | -webkit-transition: fill-opacity 250ms linear;
278 | }
279 |
280 | .nvd3 .nv-bars rect.hover {
281 | fill-opacity: 1;
282 | }
283 |
284 | .nvd3 .nv-bars .hover rect {
285 | fill: lightblue;
286 | }
287 |
288 | .nvd3 .nv-bars text {
289 | fill: rgba(0,0,0,0);
290 | }
291 |
292 | .nvd3 .nv-bars .hover text {
293 | fill: rgba(0,0,0,1);
294 | }
295 |
296 |
297 | /**********
298 | * Bars
299 | */
300 |
301 | .nvd3 .nv-multibar .nv-groups rect,
302 | .nvd3 .nv-multibarHorizontal .nv-groups rect,
303 | .nvd3 .nv-discretebar .nv-groups rect {
304 | stroke-opacity: 0;
305 |
306 | transition: fill-opacity 250ms linear;
307 | -moz-transition: fill-opacity 250ms linear;
308 | -webkit-transition: fill-opacity 250ms linear;
309 | }
310 |
311 | .nvd3 .nv-multibar .nv-groups rect:hover,
312 | .nvd3 .nv-multibarHorizontal .nv-groups rect:hover,
313 | .nvd3 .nv-discretebar .nv-groups rect:hover {
314 | fill-opacity: 1;
315 | }
316 |
317 | .nvd3 .nv-discretebar .nv-groups text,
318 | .nvd3 .nv-multibarHorizontal .nv-groups text {
319 | font-weight: bold;
320 | fill: rgba(0,0,0,1);
321 | stroke: rgba(0,0,0,0);
322 | }
323 |
324 | /***********
325 | * Pie Chart
326 | */
327 |
328 | .nvd3.nv-pie path {
329 | stroke-opacity: 0;
330 | transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
331 | -moz-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
332 | -webkit-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
333 |
334 | }
335 |
336 | .nvd3.nv-pie .nv-slice text {
337 | stroke: #000;
338 | stroke-width: 0;
339 | }
340 |
341 | .nvd3.nv-pie path {
342 | stroke: #fff;
343 | stroke-width: 1px;
344 | stroke-opacity: 1;
345 | }
346 |
347 | .nvd3.nv-pie .hover path {
348 | fill-opacity: .7;
349 | }
350 | .nvd3.nv-pie .nv-label {
351 | pointer-events: none;
352 | }
353 | .nvd3.nv-pie .nv-label rect {
354 | fill-opacity: 0;
355 | stroke-opacity: 0;
356 | }
357 |
358 | /**********
359 | * Lines
360 | */
361 |
362 | .nvd3 .nv-groups path.nv-line {
363 | fill: none;
364 | stroke-width: 1.5px;
365 | /*
366 | stroke-linecap: round;
367 | shape-rendering: geometricPrecision;
368 |
369 | transition: stroke-width 250ms linear;
370 | -moz-transition: stroke-width 250ms linear;
371 | -webkit-transition: stroke-width 250ms linear;
372 |
373 | transition-delay: 250ms
374 | -moz-transition-delay: 250ms;
375 | -webkit-transition-delay: 250ms;
376 | */
377 | }
378 |
379 | .nvd3 .nv-groups path.nv-line.nv-thin-line {
380 | stroke-width: 1px;
381 | }
382 |
383 |
384 | .nvd3 .nv-groups path.nv-area {
385 | stroke: none;
386 | /*
387 | stroke-linecap: round;
388 | shape-rendering: geometricPrecision;
389 |
390 | stroke-width: 2.5px;
391 | transition: stroke-width 250ms linear;
392 | -moz-transition: stroke-width 250ms linear;
393 | -webkit-transition: stroke-width 250ms linear;
394 |
395 | transition-delay: 250ms
396 | -moz-transition-delay: 250ms;
397 | -webkit-transition-delay: 250ms;
398 | */
399 | }
400 |
401 | .nvd3 .nv-line.hover path {
402 | stroke-width: 6px;
403 | }
404 |
405 | /*
406 | .nvd3.scatter .groups .point {
407 | fill-opacity: 0.1;
408 | stroke-opacity: 0.1;
409 | }
410 | */
411 |
412 | .nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point {
413 | fill-opacity: 0;
414 | stroke-opacity: 0;
415 | }
416 |
417 | .nvd3.nv-scatter.nv-single-point .nv-groups .nv-point {
418 | fill-opacity: .5 !important;
419 | stroke-opacity: .5 !important;
420 | }
421 |
422 |
423 | .with-transitions .nvd3 .nv-groups .nv-point {
424 | transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
425 | -moz-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
426 | -webkit-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
427 |
428 | }
429 |
430 | .nvd3.nv-scatter .nv-groups .nv-point.hover,
431 | .nvd3 .nv-groups .nv-point.hover {
432 | stroke-width: 7px;
433 | fill-opacity: .95 !important;
434 | stroke-opacity: .95 !important;
435 | }
436 |
437 |
438 | .nvd3 .nv-point-paths path {
439 | stroke: #aaa;
440 | stroke-opacity: 0;
441 | fill: #eee;
442 | fill-opacity: 0;
443 | }
444 |
445 |
446 |
447 | .nvd3 .nv-indexLine {
448 | cursor: ew-resize;
449 | }
450 |
451 |
452 | /**********
453 | * Distribution
454 | */
455 |
456 | .nvd3 .nv-distribution {
457 | pointer-events: none;
458 | }
459 |
460 |
461 |
462 | /**********
463 | * Scatter
464 | */
465 |
466 | /* **Attempting to remove this for useVoronoi(false), need to see if it's required anywhere
467 | .nvd3 .nv-groups .nv-point {
468 | pointer-events: none;
469 | }
470 | */
471 |
472 | .nvd3 .nv-groups .nv-point.hover {
473 | stroke-width: 20px;
474 | stroke-opacity: .5;
475 | }
476 |
477 | .nvd3 .nv-scatter .nv-point.hover {
478 | fill-opacity: 1;
479 | }
480 |
481 | /*
482 | .nv-group.hover .nv-point {
483 | fill-opacity: 1;
484 | }
485 | */
486 |
487 |
488 | /**********
489 | * Stacked Area
490 | */
491 |
492 | .nvd3.nv-stackedarea path.nv-area {
493 | fill-opacity: .7;
494 | /*
495 | stroke-opacity: .65;
496 | fill-opacity: 1;
497 | */
498 | stroke-opacity: 0;
499 |
500 | transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
501 | -moz-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
502 | -webkit-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
503 |
504 | /*
505 | transition-delay: 500ms;
506 | -moz-transition-delay: 500ms;
507 | -webkit-transition-delay: 500ms;
508 | */
509 |
510 | }
511 |
512 | .nvd3.nv-stackedarea path.nv-area.hover {
513 | fill-opacity: .9;
514 | /*
515 | stroke-opacity: .85;
516 | */
517 | }
518 | /*
519 | .d3stackedarea .groups path {
520 | stroke-opacity: 0;
521 | }
522 | */
523 |
524 |
525 |
526 | .nvd3.nv-stackedarea .nv-groups .nv-point {
527 | stroke-opacity: 0;
528 | fill-opacity: 0;
529 | }
530 |
531 | /*
532 | .nvd3.nv-stackedarea .nv-groups .nv-point.hover {
533 | stroke-width: 20px;
534 | stroke-opacity: .75;
535 | fill-opacity: 1;
536 | }*/
537 |
538 |
539 |
540 | /**********
541 | * Line Plus Bar
542 | */
543 |
544 | .nvd3.nv-linePlusBar .nv-bar rect {
545 | fill-opacity: .75;
546 | }
547 |
548 | .nvd3.nv-linePlusBar .nv-bar rect:hover {
549 | fill-opacity: 1;
550 | }
551 |
552 |
553 | /**********
554 | * Bullet
555 | */
556 |
557 | .nvd3.nv-bullet { font: 10px sans-serif; }
558 | .nvd3.nv-bullet .nv-measure { fill-opacity: .8; }
559 | .nvd3.nv-bullet .nv-measure:hover { fill-opacity: 1; }
560 | .nvd3.nv-bullet .nv-marker { stroke: #000; stroke-width: 2px; }
561 | .nvd3.nv-bullet .nv-markerTriangle { stroke: #000; fill: #fff; stroke-width: 1.5px; }
562 | .nvd3.nv-bullet .nv-tick line { stroke: #666; stroke-width: .5px; }
563 | .nvd3.nv-bullet .nv-range.nv-s0 { fill: #eee; }
564 | .nvd3.nv-bullet .nv-range.nv-s1 { fill: #ddd; }
565 | .nvd3.nv-bullet .nv-range.nv-s2 { fill: #ccc; }
566 | .nvd3.nv-bullet .nv-title { font-size: 14px; font-weight: bold; }
567 | .nvd3.nv-bullet .nv-subtitle { fill: #999; }
568 |
569 |
570 | .nvd3.nv-bullet .nv-range {
571 | fill: #bababa;
572 | fill-opacity: .4;
573 | }
574 | .nvd3.nv-bullet .nv-range:hover {
575 | fill-opacity: .7;
576 | }
577 |
578 |
579 |
580 | /**********
581 | * Sparkline
582 | */
583 |
584 | .nvd3.nv-sparkline path {
585 | fill: none;
586 | }
587 |
588 | .nvd3.nv-sparklineplus g.nv-hoverValue {
589 | pointer-events: none;
590 | }
591 |
592 | .nvd3.nv-sparklineplus .nv-hoverValue line {
593 | stroke: #333;
594 | stroke-width: 1.5px;
595 | }
596 |
597 | .nvd3.nv-sparklineplus,
598 | .nvd3.nv-sparklineplus g {
599 | pointer-events: all;
600 | }
601 |
602 | .nvd3 .nv-hoverArea {
603 | fill-opacity: 0;
604 | stroke-opacity: 0;
605 | }
606 |
607 | .nvd3.nv-sparklineplus .nv-xValue,
608 | .nvd3.nv-sparklineplus .nv-yValue {
609 | /*
610 | stroke: #666;
611 | */
612 | stroke-width: 0;
613 | font-size: .9em;
614 | font-weight: normal;
615 | }
616 |
617 | .nvd3.nv-sparklineplus .nv-yValue {
618 | stroke: #f66;
619 | }
620 |
621 | .nvd3.nv-sparklineplus .nv-maxValue {
622 | stroke: #2ca02c;
623 | fill: #2ca02c;
624 | }
625 |
626 | .nvd3.nv-sparklineplus .nv-minValue {
627 | stroke: #d62728;
628 | fill: #d62728;
629 | }
630 |
631 | .nvd3.nv-sparklineplus .nv-currentValue {
632 | /*
633 | stroke: #444;
634 | fill: #000;
635 | */
636 | font-weight: bold;
637 | font-size: 1.1em;
638 | }
639 |
640 | /**********
641 | * historical stock
642 | */
643 |
644 | .nvd3.nv-ohlcBar .nv-ticks .nv-tick {
645 | stroke-width: 2px;
646 | }
647 |
648 | .nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover {
649 | stroke-width: 4px;
650 | }
651 |
652 | .nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive {
653 | stroke: #2ca02c;
654 | }
655 |
656 | .nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative {
657 | stroke: #d62728;
658 | }
659 |
660 | .nvd3.nv-historicalStockChart .nv-axis .nv-axislabel {
661 | font-weight: bold;
662 | }
663 |
664 | .nvd3.nv-historicalStockChart .nv-dragTarget {
665 | fill-opacity: 0;
666 | stroke: none;
667 | cursor: move;
668 | }
669 |
670 | .nvd3 .nv-brush .extent {
671 | /*
672 | cursor: ew-resize !important;
673 | */
674 | fill-opacity: 0 !important;
675 | }
676 |
677 | .nvd3 .nv-brushBackground rect {
678 | stroke: #000;
679 | stroke-width: .4;
680 | fill: #fff;
681 | fill-opacity: .7;
682 | }
683 |
684 |
685 |
686 | /**********
687 | * Indented Tree
688 | */
689 |
690 |
691 | /**
692 | * TODO: the following 3 selectors are based on classes used in the example. I should either make them standard and leave them here, or move to a CSS file not included in the library
693 | */
694 | .nvd3.nv-indentedtree .name {
695 | margin-left: 5px;
696 | }
697 |
698 | .nvd3.nv-indentedtree .clickable {
699 | color: #08C;
700 | cursor: pointer;
701 | }
702 |
703 | .nvd3.nv-indentedtree span.clickable:hover {
704 | color: #005580;
705 | text-decoration: underline;
706 | }
707 |
708 |
709 | .nvd3.nv-indentedtree .nv-childrenCount {
710 | display: inline-block;
711 | margin-left: 5px;
712 | }
713 |
714 | .nvd3.nv-indentedtree .nv-treeicon {
715 | cursor: pointer;
716 | /*
717 | cursor: n-resize;
718 | */
719 | }
720 |
721 | .nvd3.nv-indentedtree .nv-treeicon.nv-folded {
722 | cursor: pointer;
723 | /*
724 | cursor: s-resize;
725 | */
726 | }
727 |
728 | /**********
729 | * Parallel Coordinates
730 | */
731 |
732 | .nvd3 .background path {
733 | fill: none;
734 | stroke: #ccc;
735 | stroke-opacity: .4;
736 | shape-rendering: crispEdges;
737 | }
738 |
739 | .nvd3 .foreground path {
740 | fill: none;
741 | stroke: steelblue;
742 | stroke-opacity: .7;
743 | }
744 |
745 | .nvd3 .brush .extent {
746 | fill-opacity: .3;
747 | stroke: #fff;
748 | shape-rendering: crispEdges;
749 | }
750 |
751 | .nvd3 .axis line, .axis path {
752 | fill: none;
753 | stroke: #000;
754 | shape-rendering: crispEdges;
755 | }
756 |
757 | .nvd3 .axis text {
758 | text-shadow: 0 1px 0 #fff;
759 | }
760 |
761 | /****
762 | Interactive Layer
763 | */
764 | .nvd3 .nv-interactiveGuideLine {
765 | pointer-events:none;
766 | }
767 | .nvd3 line.nv-guideline {
768 | stroke: #ccc;
769 | }
--------------------------------------------------------------------------------
/10-testing/coverage/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 10px;
3 | }
4 |
5 | .popover {
6 | max-width: none;
7 | }
8 |
9 | .glyphicon {
10 | margin-right:.25em;
11 | }
12 |
13 | .table-bordered>thead>tr>td {
14 | border-bottom-width: 1px;
15 | }
16 |
17 | .table tbody>tr>td, .table thead>tr>td {
18 | padding-top: 3px;
19 | padding-bottom: 3px;
20 | }
21 |
22 | .table-condensed tbody>tr>td {
23 | padding-top: 0;
24 | padding-bottom: 0;
25 | }
26 |
27 | .table .progress {
28 | margin-bottom: inherit;
29 | }
30 |
31 | .table-borderless th, .table-borderless td {
32 | border: 0 !important;
33 | }
34 |
35 | .table tbody td.success, li.success, span.success {
36 | background-color: #dff0d8;
37 | }
38 |
39 | .table tbody tr.danger, .table tbody td.danger, li.danger, span.danger {
40 | background-color: #f2dede;
41 | }
42 |
43 | .table tbody td.warning, li.warning, span.warning {
44 | background-color: #fcf8e3;
45 | }
46 |
47 | .table tbody td.info {
48 | background-color: #d9edf7;
49 | }
50 |
51 | td.big {
52 | width: 117px;
53 | }
54 |
55 | td.small {
56 | }
57 |
58 | td.codeLine {
59 | font-family: monospace;
60 | white-space: pre;
61 | }
62 |
63 | td span.comment {
64 | color: #888a85;
65 | }
66 |
67 | td span.default {
68 | color: #2e3436;
69 | }
70 |
71 | td span.html {
72 | color: #888a85;
73 | }
74 |
75 | td span.keyword {
76 | color: #2e3436;
77 | font-weight: bold;
78 | }
79 |
80 | pre span.string {
81 | color: #2e3436;
82 | }
83 |
84 | span.success, span.warning, span.danger {
85 | margin-right: 2px;
86 | padding-left: 10px;
87 | padding-right: 10px;
88 | text-align: center;
89 | }
90 |
91 | #classCoverageDistribution, #classComplexity {
92 | height: 200px;
93 | width: 475px;
94 | }
95 |
96 | #toplink {
97 | position: fixed;
98 | left: 5px;
99 | bottom: 5px;
100 | outline: 0;
101 | }
102 |
103 | svg text {
104 | font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif;
105 | font-size: 11px;
106 | color: #666;
107 | fill: #666;
108 | }
109 |
110 | .scrollbox {
111 | height:245px;
112 | overflow-x:hidden;
113 | overflow-y:scroll;
114 | }
--------------------------------------------------------------------------------
/10-testing/coverage/dashboard.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Dashboard for /Users/josh/Repos/modern-php/repo/10-testing/src
6 |
7 |
8 |
9 |
10 |
14 |
15 |
16 |
29 |
30 |
31 |
32 |
Classes
33 |
34 |
35 |
36 |
37 |
Coverage Distribution
38 |
39 |
40 |
41 |
42 |
43 |
Complexity
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
Insufficient Coverage
52 |
65 |
66 |
67 |
Project Risks
68 |
81 |
82 |
83 |
84 |
85 |
Methods
86 |
87 |
88 |
89 |
90 |
Coverage Distribution
91 |
92 |
93 |
94 |
95 |
96 |
Complexity
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
Insufficient Coverage
105 |
118 |
119 |
120 |
Project Risks
121 |
134 |
135 |
136 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
287 |
288 |
289 |
--------------------------------------------------------------------------------
/10-testing/coverage/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeguy/modern-php/2f6036af0165986fc36597d1ea4893cc4dc5865e/10-testing/coverage/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/10-testing/coverage/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeguy/modern-php/2f6036af0165986fc36597d1ea4893cc4dc5865e/10-testing/coverage/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/10-testing/coverage/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeguy/modern-php/2f6036af0165986fc36597d1ea4893cc4dc5865e/10-testing/coverage/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/10-testing/coverage/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Code Coverage for /Users/josh/Repos/modern-php/repo/10-testing/src
6 |
7 |
8 |
9 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | - /Users/josh/Repos/modern-php/repo/10-testing/src
21 | - (Dashboard)
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | |
33 | Code Coverage |
34 |
35 |
36 | |
37 | Lines |
38 | Functions and Methods |
39 | Classes and Traits |
40 |
41 |
42 |
43 |
44 | Total |
45 |
46 |
47 | 100.00% covered (success)
48 |
49 |
50 | |
51 | 100.00% |
52 | 13 / 13 |
53 |
54 |
55 | 100.00% covered (success)
56 |
57 |
58 | |
59 | 100.00% |
60 | 3 / 3 |
61 |
62 |
63 | 100.00% covered (success)
64 |
65 |
66 | |
67 | 100.00% |
68 | 1 / 1 |
69 |
70 |
71 |
72 | Whovian.php |
73 |
74 |
75 | 100.00% covered (success)
76 |
77 |
78 | |
79 | 100.00% |
80 | 13 / 13 |
81 |
82 |
83 | 100.00% covered (success)
84 |
85 |
86 | |
87 | 100.00% |
88 | 3 / 3 |
89 |
90 |
91 | 100.00% covered (success)
92 |
93 |
94 | |
95 | 100.00% |
96 | 1 / 1 |
97 |
98 |
99 |
100 |
101 |
102 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/10-testing/coverage/js/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.3.0 (http://getbootstrap.com)
3 | * Copyright 2011-2014 Twitter, Inc.
4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
5 | */
6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.0",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.0",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus","focus"==b.type)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.0",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c="prev"==a?-1:1,d=this.getItemIndex(b),e=(d+c)%this.$items.length;return this.$items.eq(e)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i="next"==b?"first":"last",j=this;if(!f.length){if(!this.options.wrap)return;f=this.$element.find(".item")[i]()}if(f.hasClass("active"))return this.sliding=!1;var k=f[0],l=a.Event("slide.bs.carousel",{relatedTarget:k,direction:h});if(this.$element.trigger(l),!l.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var m=a(this.$indicators.children()[this.getItemIndex(f)]);m&&m.addClass("active")}var n=a.Event("slid.bs.carousel",{relatedTarget:k,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),j.sliding=!1,setTimeout(function(){j.$element.trigger(n)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(n)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a(this.options.trigger).filter('[href="#'+b.id+'"], [data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.0",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0,trigger:'[data-toggle="collapse"]'},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.find("> .panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":a.extend({},e.data(),{trigger:this});c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.0",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(b){if(/(38|40|27|32)/.test(b.which)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var e=c(d),g=e.hasClass("open");if(!g&&27!=b.which||g&&27==b.which)return 27==b.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.divider):visible a",i=e.find('[role="menu"]'+h+', [role="listbox"]'+h);if(i.length){var j=i.index(b.target);38==b.which&&j>0&&j--,40==b.which&&j').prependTo(this.$element).on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),f&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;f?this.$backdrop.one("bsTransitionEnd",b).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):b()}else if(!this.isShown&&this.$backdrop){this.$backdrop.removeClass("in");var g=function(){d.removeBackdrop(),b&&b()};a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one("bsTransitionEnd",g).emulateTransitionEnd(c.BACKDROP_TRANSITION_DURATION):g()}else b&&b()},c.prototype.checkScrollbar=function(){this.scrollbarWidth=this.measureScrollbar()},c.prototype.setScrollbar=function(){var a=parseInt(this.$body.css("padding-right")||0,10);this.scrollbarWidth&&this.$body.css("padding-right",a+this.scrollbarWidth)},c.prototype.resetScrollbar=function(){this.$body.css("padding-right","")},c.prototype.measureScrollbar=function(){if(document.body.clientWidth>=window.innerWidth)return 0;var a=document.createElement("div");a.className="modal-scrollbar-measure",this.$body.append(a);var b=a.offsetWidth-a.clientWidth;return this.$body[0].removeChild(a),b};var d=a.fn.modal;a.fn.modal=b,a.fn.modal.Constructor=c,a.fn.modal.noConflict=function(){return a.fn.modal=d,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(c){var d=a(this),e=d.attr("href"),f=a(d.attr("data-target")||e&&e.replace(/.*(?=#[^\s]+$)/,"")),g=f.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(e)&&e},f.data(),d.data());d.is("a")&&c.preventDefault(),f.one("show.bs.modal",function(a){a.isDefaultPrevented()||f.one("hidden.bs.modal",function(){d.is(":visible")&&d.trigger("focus")})}),b.call(f,g,this)})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof b&&b,g=f&&f.selector;(e||"destroy"!=b)&&(g?(e||d.data("bs.tooltip",e={}),e[g]||(e[g]=new c(this,f))):e||d.data("bs.tooltip",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};c.VERSION="3.3.0",c.TRANSITION_DURATION=150,c.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(this.options.viewport.selector||this.options.viewport);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c&&c.$tip&&c.$tip.is(":visible")?void(c.hoverState="in"):(c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.options.container?a(this.options.container):this.$element.parent(),p=this.getPosition(o);h="bottom"==h&&k.bottom+m>p.bottom?"top":"top"==h&&k.top-mp.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.width&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type)})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b,g=f&&f.selector;(e||"destroy"!=b)&&(g?(e||d.data("bs.popover",e={}),e[g]||(e[g]=new c(this,f))):e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.0",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},c.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){var e=a.proxy(this.process,this);this.$body=a("body"),this.$scrollElement=a(a(c).is("body")?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",e),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.0",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b="offset",c=0;a.isWindow(this.$scrollElement[0])||(b="position",c=this.$scrollElement.scrollTop()),this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight();var d=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+c,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){d.offsets.push(this[0]),d.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,this.clear();var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")},b.prototype.clear=function(){a(this.selector).parentsUntil(this.options.target,".active").removeClass("active")};var d=a.fn.scrollspy;a.fn.scrollspy=c,a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=d,this},a(window).on("load.bs.scrollspy.data-api",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);c.call(b,b.data())})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new c(this)),"string"==typeof b&&e[b]()})}var c=function(b){this.element=a(b)};c.VERSION="3.3.0",c.TRANSITION_DURATION=150,c.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a"),f=a.Event("hide.bs.tab",{relatedTarget:b[0]}),g=a.Event("show.bs.tab",{relatedTarget:e[0]});if(e.trigger(f),b.trigger(g),!g.isDefaultPrevented()&&!f.isDefaultPrevented()){var h=a(d);this.activate(b.closest("li"),c),this.activate(h,h.parent(),function(){e.trigger({type:"hidden.bs.tab",relatedTarget:b[0]}),b.trigger({type:"shown.bs.tab",relatedTarget:e[0]})})}}},c.prototype.activate=function(b,d,e){function f(){g.removeClass("active").find("> .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)
7 | }(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.0",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=i?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=a("body").height();"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery);
--------------------------------------------------------------------------------
/10-testing/coverage/js/holder.js:
--------------------------------------------------------------------------------
1 | /*
2 | Holder.js - client side image placeholders
3 | © 2012-2014 Ivan Malopinsky - http://imsky.co
4 | */
5 | (function(register, global, undefined) {
6 |
7 | //Constants and definitions
8 |
9 | var SVG_NS = 'http://www.w3.org/2000/svg';
10 | var document = global.document;
11 |
12 | var Holder = {
13 | /**
14 | * Adds a theme to default settings
15 | *
16 | * @param {string} name Theme name
17 | * @param {Object} theme Theme object, with foreground, background, size, font, and fontweight properties.
18 | */
19 | addTheme: function(name, theme) {
20 | name != null && theme != null && (App.settings.themes[name] = theme);
21 | delete App.vars.cache.themeKeys;
22 | return this;
23 | },
24 |
25 | /**
26 | * Appends a placeholder to an element
27 | *
28 | * @param {string} src Placeholder URL string
29 | * @param {string} el Selector of target element(s)
30 | */
31 | addImage: function(src, el) {
32 | var node = document.querySelectorAll(el);
33 | if (node.length) {
34 | for (var i = 0, l = node.length; i < l; i++) {
35 | var img = newEl('img');
36 | setAttr(img, {
37 | 'data-src': src
38 | });
39 | node[i].appendChild(img);
40 | }
41 | }
42 | return this;
43 | },
44 |
45 | /**
46 | * Runs Holder with options. By default runs Holder on all images with "holder.js" in their source attributes.
47 | *
48 | * @param {Object} userOptions Options object, can contain domain, themes, images, and bgnodes properties
49 | */
50 | run: function(userOptions) {
51 | userOptions = userOptions || {};
52 | var renderSettings = {};
53 |
54 | App.vars.preempted = true;
55 |
56 | var options = extend(App.settings, userOptions);
57 |
58 | renderSettings.renderer = options.renderer ? options.renderer : App.setup.renderer;
59 | if (App.setup.renderers.join(',').indexOf(renderSettings.renderer) === -1) {
60 | renderSettings.renderer = App.setup.supportsSVG ? 'svg' : (App.setup.supportsCanvas ? 'canvas' : 'html');
61 | }
62 |
63 | //< v2.4 API compatibility
64 | if (options.use_canvas) {
65 | renderSettings.renderer = 'canvas';
66 | } else if (options.use_svg) {
67 | renderSettings.renderer = 'svg';
68 | }
69 |
70 | var images = getNodeArray(options.images);
71 | var bgnodes = getNodeArray(options.bgnodes);
72 | var stylenodes = getNodeArray(options.stylenodes);
73 | var objects = getNodeArray(options.objects);
74 |
75 | renderSettings.stylesheets = [];
76 | renderSettings.svgXMLStylesheet = true;
77 | renderSettings.noFontFallback = options.noFontFallback ? options.noFontFallback : false;
78 |
79 | for (var i = 0; i < stylenodes.length; i++) {
80 | var styleNode = stylenodes[i];
81 | if (styleNode.attributes.rel && styleNode.attributes.href && styleNode.attributes.rel.value == 'stylesheet') {
82 | var href = styleNode.attributes.href.value;
83 | //todo: write isomorphic relative-to-absolute URL function
84 | var proxyLink = newEl('a');
85 | proxyLink.href = href;
86 | var stylesheetURL = proxyLink.protocol + '//' + proxyLink.host + proxyLink.pathname + proxyLink.search;
87 | renderSettings.stylesheets.push(stylesheetURL);
88 | }
89 | }
90 |
91 | for (i = 0; i < bgnodes.length; i++) {
92 | var backgroundImage = global.getComputedStyle(bgnodes[i], null).getPropertyValue('background-image');
93 | var dataBackgroundImage = bgnodes[i].getAttribute('data-background-src');
94 | var rawURL = null;
95 |
96 | if (dataBackgroundImage == null) {
97 | rawURL = backgroundImage;
98 | } else {
99 | rawURL = dataBackgroundImage;
100 | }
101 |
102 | var holderURL = null;
103 | var holderString = '?' + options.domain + '/';
104 |
105 | if (rawURL.indexOf(holderString) === 0) {
106 | holderURL = rawURL.slice(1);
107 | } else if (rawURL.indexOf(holderString) != -1) {
108 | var fragment = rawURL.substr(rawURL.indexOf(holderString)).slice(1);
109 | var fragmentMatch = fragment.match(/([^\"]*)"?\)/);
110 |
111 | if (fragmentMatch != null) {
112 | holderURL = fragmentMatch[1];
113 | }
114 | }
115 |
116 | if (holderURL != null) {
117 | var holderFlags = parseURL(holderURL, options);
118 | if (holderFlags) {
119 | prepareDOMElement('background', bgnodes[i], holderFlags, renderSettings);
120 | }
121 | }
122 | }
123 |
124 | for (i = 0; i < objects.length; i++) {
125 | var object = objects[i];
126 | var objectAttr = {};
127 |
128 | try {
129 | objectAttr.data = object.getAttribute('data');
130 | objectAttr.dataSrc = object.getAttribute('data-src');
131 | } catch (e) {}
132 |
133 | var objectHasSrcURL = objectAttr.data != null && objectAttr.data.indexOf(options.domain) === 0;
134 | var objectHasDataSrcURL = objectAttr.dataSrc != null && objectAttr.dataSrc.indexOf(options.domain) === 0;
135 |
136 | if (objectHasSrcURL) {
137 | prepareImageElement(options, renderSettings, objectAttr.data, object);
138 | } else if (objectHasDataSrcURL) {
139 | prepareImageElement(options, renderSettings, objectAttr.dataSrc, object);
140 | }
141 | }
142 |
143 | for (i = 0; i < images.length; i++) {
144 | var image = images[i];
145 | var imageAttr = {};
146 |
147 | try {
148 | imageAttr.src = image.getAttribute('src');
149 | imageAttr.dataSrc = image.getAttribute('data-src');
150 | imageAttr.rendered = image.getAttribute('data-holder-rendered');
151 | } catch (e) {}
152 |
153 | var imageHasSrc = imageAttr.src != null;
154 | var imageHasDataSrcURL = imageAttr.dataSrc != null && imageAttr.dataSrc.indexOf(options.domain) === 0;
155 | var imageRendered = imageAttr.rendered != null && imageAttr.rendered == 'true';
156 |
157 | if (imageHasSrc) {
158 | if (imageAttr.src.indexOf(options.domain) === 0) {
159 | prepareImageElement(options, renderSettings, imageAttr.src, image);
160 | } else if (imageHasDataSrcURL) {
161 | //Image has a valid data-src and an invalid src
162 | if (imageRendered) {
163 | //If the placeholder has already been render, re-render it
164 | prepareImageElement(options, renderSettings, imageAttr.dataSrc, image);
165 | } else {
166 | //If the placeholder has not been rendered, check if the image exists and render a fallback if it doesn't
167 | (function(src, options, renderSettings, dataSrc, image){
168 | imageExists(src, function(exists){
169 | if(!exists){
170 | prepareImageElement(options, renderSettings, dataSrc, image);
171 | }
172 | });
173 | })(imageAttr.src, options, renderSettings, imageAttr.dataSrc, image);
174 | }
175 | }
176 | } else if (imageHasDataSrcURL) {
177 | prepareImageElement(options, renderSettings, imageAttr.dataSrc, image);
178 | }
179 | }
180 |
181 | return this;
182 | },
183 | //todo: remove invisibleErrorFn for 2.5
184 | invisibleErrorFn: function(fn) {
185 | return function(el) {
186 | if (el.hasAttribute('data-holder-invisible')) {
187 | throw 'Holder: invisible placeholder';
188 | }
189 | };
190 | }
191 | };
192 |
193 | //< v2.4 API compatibility
194 |
195 | Holder.add_theme = Holder.addTheme;
196 | Holder.add_image = Holder.addImage;
197 | Holder.invisible_error_fn = Holder.invisibleErrorFn;
198 |
199 | var App = {
200 | settings: {
201 | domain: 'holder.js',
202 | images: 'img',
203 | objects: 'object',
204 | bgnodes: 'body .holderjs',
205 | stylenodes: 'head link.holderjs',
206 | stylesheets: [],
207 | themes: {
208 | 'gray': {
209 | background: '#EEEEEE',
210 | foreground: '#AAAAAA'
211 | },
212 | 'social': {
213 | background: '#3a5a97',
214 | foreground: '#FFFFFF'
215 | },
216 | 'industrial': {
217 | background: '#434A52',
218 | foreground: '#C2F200'
219 | },
220 | 'sky': {
221 | background: '#0D8FDB',
222 | foreground: '#FFFFFF'
223 | },
224 | 'vine': {
225 | background: '#39DBAC',
226 | foreground: '#1E292C'
227 | },
228 | 'lava': {
229 | background: '#F8591A',
230 | foreground: '#1C2846'
231 | }
232 | }
233 | },
234 | defaults: {
235 | size: 10,
236 | units: 'pt',
237 | scale: 1/16
238 | },
239 | flags: {
240 | dimensions: {
241 | regex: /^(\d+)x(\d+)$/,
242 | output: function(val) {
243 | var exec = this.regex.exec(val);
244 | return {
245 | width: +exec[1],
246 | height: +exec[2]
247 | };
248 | }
249 | },
250 | fluid: {
251 | regex: /^([0-9]+%?)x([0-9]+%?)$/,
252 | output: function(val) {
253 | var exec = this.regex.exec(val);
254 | return {
255 | width: exec[1],
256 | height: exec[2]
257 | };
258 | }
259 | },
260 | colors: {
261 | regex: /(?:#|\^)([0-9a-f]{3,})\:(?:#|\^)([0-9a-f]{3,})/i,
262 | output: function(val) {
263 | var exec = this.regex.exec(val);
264 | return {
265 | foreground: '#' + exec[2],
266 | background: '#' + exec[1]
267 | };
268 | }
269 | },
270 | text: {
271 | regex: /text\:(.*)/,
272 | output: function(val) {
273 | return this.regex.exec(val)[1].replace('\\/', '/');
274 | }
275 | },
276 | font: {
277 | regex: /font\:(.*)/,
278 | output: function(val) {
279 | return this.regex.exec(val)[1];
280 | }
281 | },
282 | auto: {
283 | regex: /^auto$/
284 | },
285 | textmode: {
286 | regex: /textmode\:(.*)/,
287 | output: function(val) {
288 | return this.regex.exec(val)[1];
289 | }
290 | },
291 | random: {
292 | regex: /^random$/
293 | }
294 | }
295 | };
296 |
297 | /**
298 | * Processes provided source attribute and sets up the appropriate rendering workflow
299 | *
300 | * @private
301 | * @param options Instance options from Holder.run
302 | * @param renderSettings Instance configuration
303 | * @param src Image URL
304 | * @param el Image DOM element
305 | */
306 | function prepareImageElement(options, renderSettings, src, el) {
307 | var holderFlags = parseURL(src.substr(src.lastIndexOf(options.domain)), options);
308 | if (holderFlags) {
309 | prepareDOMElement(null, el, holderFlags, renderSettings);
310 | }
311 | }
312 |
313 | /**
314 | * Processes a Holder URL and extracts flags
315 | *
316 | * @private
317 | * @param url URL
318 | * @param options Instance options from Holder.run
319 | */
320 | function parseURL(url, options) {
321 | var ret = {
322 | theme: extend(App.settings.themes.gray, null),
323 | stylesheets: options.stylesheets,
324 | holderURL: []
325 | };
326 | var render = false;
327 | var vtab = String.fromCharCode(11);
328 | var flags = url.replace(/([^\\])\//g, '$1' + vtab).split(vtab);
329 | var uriRegex = /%[0-9a-f]{2}/gi;
330 | for (var fl = flags.length, j = 0; j < fl; j++) {
331 | var flag = flags[j];
332 | if (flag.match(uriRegex)) {
333 | try {
334 | flag = decodeURIComponent(flag);
335 | } catch (e) {
336 | flag = flags[j];
337 | }
338 | }
339 |
340 | var push = false;
341 |
342 | if (App.flags.dimensions.match(flag)) {
343 | render = true;
344 | ret.dimensions = App.flags.dimensions.output(flag);
345 | push = true;
346 | } else if (App.flags.fluid.match(flag)) {
347 | render = true;
348 | ret.dimensions = App.flags.fluid.output(flag);
349 | ret.fluid = true;
350 | push = true;
351 | } else if (App.flags.textmode.match(flag)) {
352 | ret.textmode = App.flags.textmode.output(flag);
353 | push = true;
354 | } else if (App.flags.colors.match(flag)) {
355 | var colors = App.flags.colors.output(flag);
356 | ret.theme = extend(ret.theme, colors);
357 | //todo: convert implicit theme use to a theme: flag
358 | push = true;
359 | } else if (options.themes[flag]) {
360 | //If a theme is specified, it will override custom colors
361 | if (options.themes.hasOwnProperty(flag)) {
362 | ret.theme = extend(options.themes[flag], null);
363 | }
364 | push = true;
365 | } else if (App.flags.font.match(flag)) {
366 | ret.font = App.flags.font.output(flag);
367 | push = true;
368 | } else if (App.flags.auto.match(flag)) {
369 | ret.auto = true;
370 | push = true;
371 | } else if (App.flags.text.match(flag)) {
372 | ret.text = App.flags.text.output(flag);
373 | push = true;
374 | } else if (App.flags.random.match(flag)) {
375 | if (App.vars.cache.themeKeys == null) {
376 | App.vars.cache.themeKeys = Object.keys(options.themes);
377 | }
378 | var theme = App.vars.cache.themeKeys[0 | Math.random() * App.vars.cache.themeKeys.length];
379 | ret.theme = extend(options.themes[theme], null);
380 | push = true;
381 | }
382 |
383 | if (push) {
384 | ret.holderURL.push(flag);
385 | }
386 | }
387 | ret.holderURL.unshift(options.domain);
388 | ret.holderURL = ret.holderURL.join('/');
389 | return render ? ret : false;
390 | }
391 |
392 | /**
393 | * Modifies the DOM to fit placeholders and sets up resizable image callbacks (for fluid and automatically sized placeholders)
394 | *
395 | * @private
396 | * @param el Image DOM element
397 | * @param flags Placeholder-specific configuration
398 | * @param _renderSettings Instance configuration
399 | */
400 | function prepareDOMElement(mode, el, flags, _renderSettings) {
401 | var dimensions = flags.dimensions,
402 | theme = flags.theme;
403 | var dimensionsCaption = dimensions.width + 'x' + dimensions.height;
404 | mode = mode == null ? (flags.fluid ? 'fluid' : 'image') : mode;
405 |
406 | if (flags.text != null) {
407 | theme.text = flags.text;
408 |
409 | //